Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-05-08 21:12:23 +00:00
parent f76e1491c9
commit 469adbda3d
145 changed files with 1770 additions and 1038 deletions

View File

@ -21,7 +21,7 @@ import CiResourceHeaderSkeletonLoader from './ci_resource_header_skeleton_loader
export default {
i18n: {
moreActionsLabel: __('More actions'),
reportAbuse: __('Report abuse to administrator'),
reportAbuse: __('Report abuse'),
lastRelease: s__('CiCatalog|Released %{date}'),
lastReleaseMissing: s__('CiCatalog|No release available'),
},

View File

@ -319,7 +319,7 @@ export default {
/>
<a
:v-once="!viewDiffsFileByFile"
class="gl-mr-2 gl-text-decoration-none! gl-word-break-all"
class="gl-mr-2 gl-text-decoration-none! gl-break-all"
:href="titleLink"
data-testid="file-title"
@click="handleFileNameClick"

View File

@ -34,6 +34,14 @@ const getMRTargetProject = () => {
return url.searchParams.get('target_project') || '';
};
const getCrossOriginExtensionHostFlagValue = (extensionsGallerySettings) => {
return (
extensionsGallerySettings?.enabled ||
extensionsGallerySettings?.reason === 'opt_in_unset' ||
extensionsGallerySettings?.reason === 'opt_in_disabled'
);
};
export const initGitlabWebIDE = async (el) => {
// what: Pull what we need from the element. We will replace it soon.
const {
@ -87,6 +95,7 @@ export const initGitlabWebIDE = async (el) => {
},
featureFlags: {
settingsSync: true,
crossOriginExtensionHost: getCrossOriginExtensionHostFlagValue(extensionsGallerySettings),
},
editorFont,
extensionsGallerySettings,

View File

@ -55,7 +55,7 @@ export default {
promoteSuccessMessage: __(
'The issue was successfully promoted to an epic. Redirecting to epic...',
),
reportAbuse: __('Report abuse to administrator'),
reportAbuse: __('Report abuse'),
referenceFetchError: __('An error occurred while fetching reference'),
copyReferenceText: __('Copy reference'),
},

View File

@ -83,7 +83,7 @@ export default {
<template #left-primary>
<div class="gl-display-flex gl-align-items-center">
<router-link
class="gl-text-body gl-font-weight-bold gl-word-break-all"
class="gl-text-body gl-font-weight-bold gl-break-all"
data-testid="name"
:to="linkTo"
>

View File

@ -43,7 +43,7 @@ export default {
<template>
<title-area :metadata-loading="tagsLoading">
<template #title>
<span class="gl-word-break-all" data-testid="title">
<span class="gl-break-all" data-testid="title">
{{ artifactDetail.digest }}
</span>
</template>

View File

@ -104,7 +104,7 @@ export default {
>
<gl-link
v-if="containsWebPathLink"
class="gl-text-body gl-min-w-0 gl-word-break-all"
class="gl-text-body gl-min-w-0 gl-break-all"
:class="errorPackageStyle"
:href="packageLink"
>

View File

@ -151,7 +151,7 @@ export default {
<router-link
v-if="containsWebPathLink"
:class="errorPackageStyle"
class="gl-text-body gl-min-w-0 gl-word-break-all"
class="gl-text-body gl-min-w-0 gl-break-all"
data-testid="details-link"
:to="{ name: 'details', params: { id: packageId } }"
>

View File

@ -789,7 +789,11 @@ export default {
<project-setting-row
ref="pipeline-settings"
:label="$options.i18n.ciCdLabel"
:help-text="s__('ProjectSettings|Build, test, and deploy your changes.')"
:help-text="
s__(
'ProjectSettings|Build, test, and deploy your changes. Does not apply to project integrations.',
)
"
>
<project-feature-setting
v-model="buildsAccessLevel"

View File

@ -14,7 +14,7 @@ export default {
},
inject: ['reportedUserId', 'reportedFromUrl'],
i18n: {
reportAbuse: s__('ReportAbuse|Report abuse to administrator'),
reportAbuse: s__('ReportAbuse|Report abuse'),
},
data() {
return {

View File

@ -286,8 +286,8 @@ export default {
</gl-link>
</gl-alert>
<releases-empty-state v-if="shouldRenderEmptyState" />
<div v-else class="gl-align-self-end gl-mb-3 gl-display-flex">
<releases-sort :value="sort" class="gl-mr-2" @input="onSortChanged" />
<div v-else class="gl-align-self-end gl-display-flex gl-gap-3">
<releases-sort :value="sort" @input="onSortChanged" />
<div
v-if="newReleasePath"
@ -313,7 +313,7 @@ export default {
:class="{ 'linked-card': releases.length > 1 && index !== releases.length - 1 }"
/>
<release-skeleton-loader v-if="shouldRenderLoadingIndicator" />
<release-skeleton-loader v-if="shouldRenderLoadingIndicator" class="gl-mt-5" />
<releases-pagination
v-if="shouldRenderPagination"
@ -331,6 +331,6 @@ export default {
height: 17px;
top: 100%;
position: absolute;
left: 32px;
left: 23px;
}
</style>

View File

@ -59,14 +59,12 @@ export default {
<template>
<div>
<div class="card-text gl-mt-3">
<b>{{ __('Evidence collection') }}</b>
</div>
<div v-for="(evidence, index) in evidences" :key="evidenceTitle(index)" class="mb-2">
<div class="d-flex gl-align-items-center">
<h3 class="gl-heading-5 gl-mb-2!">{{ __('Evidence collection') }}</h3>
<div v-for="(evidence, index) in evidences" :key="evidenceTitle(index)">
<div class="gl-display-flex gl-align-items-center">
<gl-link
v-gl-tooltip
class="d-flex gl-align-items-center monospace"
class="gl-display-flex gl-align-items-center gl-font-monospace"
target="_blank"
:title="__('Open evidence JSON in new tab')"
:href="evidenceUrl(index)"
@ -76,18 +74,18 @@ export default {
<gl-icon name="external-link" class="gl-ml-2 gl-flex-shrink-0 gl-flex-grow-0" />
</gl-link>
<expand-button class="gl-ml-4">
<expand-button class="gl-display-flex gl-align-items-center gl-gap-2 gl-ml-4">
<template #short>
<span class="js-short monospace">{{ shortSha(index) }}</span>
<span class="js-short gl-font-monospace gl-text-secondary">{{ shortSha(index) }}</span>
</template>
<template #expanded>
<span class="js-expanded monospace gl-pl-2">{{ sha(index) }}</span>
<span class="js-expanded gl-font-monospace gl-pl-2">{{ sha(index) }}</span>
</template>
</expand-button>
<clipboard-button :title="__('Copy evidence SHA')" :text="sha(index)" category="tertiary" />
</div>
<div class="d-flex gl-align-items-center text-muted">
<div class="gl-display-flex gl-align-items-center gl-text-secondary">
<gl-icon
v-gl-tooltip
name="clock"

View File

@ -1,4 +1,5 @@
<script>
import { GlCard } from '@gitlab/ui';
import { isEmpty } from 'lodash';
import SafeHtml from '~/vue_shared/directives/safe_html';
import { scrollToElement } from '~/lib/utils/common_utils';
@ -16,6 +17,7 @@ import ReleaseBlockMilestoneInfo from './release_block_milestone_info.vue';
export default {
name: 'ReleaseBlock',
components: {
GlCard,
EvidenceBlock,
ReleaseBlockAssets,
ReleaseBlockFooter,
@ -92,10 +94,24 @@ export default {
};
</script>
<template>
<div :id="htmlId" :class="{ 'bg-line-target-blue': isHighlighted }" class="card release-block">
<release-block-header :release="release" />
<div class="card-body">
<div v-if="shouldRenderMilestoneInfo">
<gl-card
:id="htmlId"
:class="{ 'bg-line-target-blue': isHighlighted }"
class="gl-new-card"
header-class="gl-new-card-header"
body-class="gl-new-card-body gl-px-5 gl-py-4"
footer-class="gl-bg-white"
data-testid="release-block"
>
<template #header>
<release-block-header :release="release" />
</template>
<div class="gl-display-flex gl-flex-direction-column gl-gap-5">
<div
v-if="shouldRenderMilestoneInfo"
class="gl-border-b-solid gl-border-b-1 gl-border-gray-100"
>
<!-- TODO: Switch open* links to opened* once fields have been updated in GraphQL -->
<release-block-milestone-info
:milestones="milestones"
@ -105,27 +121,35 @@ export default {
:merged-merge-requests-path="release._links.mergedMergeRequestsUrl"
:closed-merge-requests-path="release._links.closedMergeRequestsUrl"
/>
<hr class="mb-3 mt-0" />
</div>
<release-block-assets v-if="shouldRenderAssets" :assets="assets" />
<release-block-assets
v-if="shouldRenderAssets"
:assets="assets"
:class="{
'gl-pb-5 gl-border-b-solid gl-border-b-1 gl-border-gray-100':
hasEvidence || release.descriptionHtml,
}"
/>
<evidence-block v-if="hasEvidence" :release="release" />
<div ref="gfm-content" class="card-text gl-mt-3">
<div v-if="release.descriptionHtml" ref="gfm-content">
<h3 class="gl-heading-5 gl-mb-2!">{{ __('Release notes') }}</h3>
<div v-safe-html:[$options.safeHtmlConfig]="release.descriptionHtml" class="md"></div>
</div>
</div>
<release-block-footer
class="card-footer"
:commit="release.commit"
:commit-path="release.commitPath"
:tag-name="release.tagName"
:tag-path="release.tagPath"
:author="release.author"
:released-at="release.releasedAt"
:created-at="release.createdAt"
:sort="sort"
/>
</div>
<template #footer>
<release-block-footer
:commit="release.commit"
:commit-path="release.commitPath"
:tag-name="release.tagName"
:tag-path="release.tagPath"
:author="release.author"
:released-at="release.releasedAt"
:created-at="release.createdAt"
:sort="sort"
/>
</template>
</gl-card>
</template>

View File

@ -98,11 +98,12 @@ export default {
</script>
<template>
<div class="card-text gl-mt-3">
<div>
<gl-button
data-testid="accordion-button"
variant="link"
class="gl-font-weight-bold gl-text-black-normal!"
class="gl-text-black-normal!"
button-text-classes="gl-heading-5"
@click="toggleAssetsExpansion"
>
<gl-icon

View File

@ -85,14 +85,14 @@ export default {
};
</script>
<template>
<div class="gl-display-flex gl-gap-5">
<div v-if="commit" class="gl-display-flex gl-align-items-center js-commit-info">
<gl-icon ref="commitIcon" name="commit" class="gl-mr-2 gl-text-gray-700" />
<div class="gl-display-flex gl-gap-5 gl-align-items-center gl-font-sm">
<div v-if="commit" class="gl-display-flex gl-align-items-center gl-gap-2 js-commit-info">
<gl-icon ref="commitIcon" name="commit" class="gl-text-secondary" />
<div v-gl-tooltip.bottom :title="commit.title">
<gl-link
v-if="commitPath"
:href="commitPath"
class="gl-font-sm gl-font-monospace gl-mr-0 gl-text-gray-700"
class="gl-font-monospace gl-text-secondary gl-mr-0"
>
{{ commit.shortId }}
</gl-link>
@ -100,23 +100,16 @@ export default {
</div>
</div>
<div v-if="tagName" class="gl-display-flex gl-align-items-center js-tag-info">
<gl-icon name="tag" class="gl-mr-2 gl-text-gray-700" />
<div v-if="tagName" class="gl-display-flex gl-align-items-center gl-gap-2 js-tag-info">
<gl-icon name="tag" class="gl-text-secondary" />
<div v-gl-tooltip.bottom :title="__('Tag')">
<gl-link
v-if="tagPath"
:href="tagPath"
class="gl-font-sm gl-font-monospace gl-mr-0 gl-text-gray-700"
>
<gl-link v-if="tagPath" :href="tagPath" class="gl-font-monospace gl-mr-0 gl-text-secondary">
{{ tagName }}
</gl-link>
<span v-else>{{ tagName }}</span>
</div>
</div>
<div
v-if="timeAt || author"
class="gl-display-flex gl-align-items-center js-author-date-info gl-font-sm"
>
<div v-if="timeAt || author" class="gl-display-flex gl-align-items-center js-author-date-info">
<span class="gl-text-secondary">{{ createdTime }}&nbsp;</span>
<template v-if="timeAt">
<span
@ -128,7 +121,7 @@ export default {
</span>
</template>
<div v-if="author" class="gl-display-flex">
<div v-if="author" class="gl-display-flex gl-align-items-center gl-gap-1">
<span class="gl-text-secondary">{{ __('by') }}&nbsp;</span>
<user-avatar-link
:link-href="author.webUrl"

View File

@ -48,8 +48,11 @@ export default {
</script>
<template>
<div class="card-header d-flex gl-align-items-center bg-white pr-0">
<h2 class="card-title gl-my-2 mr-auto gl-font-size-h2">
<div class="gl-display-flex gl-align-items-center gl-justify-content-space-between gl-w-full">
<h2
class="gl-new-card-title gl-heading-3 gl-m-0! gl-display-flex gl-gap-3"
data-testid="release-block-title"
>
<gl-link v-if="selfLink" class="gl-text-black-normal" :href="selfLink">
{{ release.name }}
</gl-link>
@ -63,17 +66,17 @@ export default {
'Private - Guest users are not allowed to view detailed release information like title and source code.',
)
"
class="text-secondary gl-mb-2"
class="gl-text-secondary"
/>
</template>
<gl-badge v-if="release.upcomingRelease" variant="warning" class="align-middle">{{
<gl-badge v-if="release.upcomingRelease" variant="warning" class="gl-align-self-center">{{
__('Upcoming Release')
}}</gl-badge>
<gl-badge
v-else-if="release.historicalRelease"
v-gl-tooltip
:title="$options.i18n.historicalTooltip"
class="gl-align-middle"
class="gl-align-self-center"
>
{{ $options.i18n.historical }}
</gl-badge>
@ -83,7 +86,7 @@ export default {
category="primary"
size="small"
variant="default"
class="gl-mr-3 js-edit-button gl-ml-3 gl-pb-3"
class="js-edit-button"
:href="editLink"
>
{{ $options.i18n.editButton }}

View File

@ -132,10 +132,10 @@ export default {
};
</script>
<template>
<div class="release-block-milestone-info gl-display-flex gl-flex-wrap">
<div class="release-block-milestone-info gl-display-flex gl-flex-wrap gl-gap-6">
<div
v-gl-tooltip
class="milestone-progress-bar-container js-milestone-progress-bar-container gl-display-flex gl-flex-direction-column gl-mr-6 gl-mb-5"
class="milestone-progress-bar-container js-milestone-progress-bar-container gl-display-flex gl-flex-direction-column"
:title="__('Closed issues')"
>
<span class="gl-mb-3">{{ percentCompleteText }}</span>
@ -143,9 +143,7 @@ export default {
<gl-progress-bar :value="issueCounts.closed" :max="issueCounts.total" />
</span>
</div>
<div
class="gl-display-flex gl-flex-direction-column gl-mr-6 gl-mb-5 js-milestone-list-container"
>
<div class="gl-display-flex gl-flex-direction-column js-milestone-list-container">
<span class="gl-mb-2">{{ milestoneLabelText }}</span>
<div class="gl-display-flex gl-flex-wrap gl-align-items-flex-end">
<template v-for="(milestone, index) in milestonesToDisplay">

View File

@ -96,7 +96,7 @@ export default {
v-safe-html:[$options.safeHtmlConfig]="commit.titleHtml"
:href="commit.webPath"
:class="{ 'gl-italic': !commit.message }"
class="commit-row-message item-title gl-line-clamp-1 gl-word-break-all!"
class="commit-row-message item-title gl-line-clamp-1 !gl-break-all"
/>
<gl-button
v-if="commit.descriptionHtml"

View File

@ -34,7 +34,7 @@ export default {
class="gl-rounded-base gl-min-w-5 gl-h-5 gl-display-inline-block gl-align-bottom gl-mr-3"
:style="{ 'background-color': label.color }"
></span>
<span class="gl-reset-text-align gl-m-0 gl-p-0 label-title gl-word-break-all">{{
<span class="gl-reset-text-align gl-m-0 gl-p-0 label-title gl-break-all">{{
label.title
}}</span></gl-form-checkbox
>

View File

@ -58,7 +58,7 @@ export default {
</gl-badge>
</span>
<span class="gl-text-gray-500 gl-word-break-all">@{{ user.username }}</span>
<span class="gl-text-gray-500 gl-break-all">@{{ user.username }}</span>
<span
v-if="user.status.customized"

View File

@ -13,42 +13,43 @@ import {
import { createAlert } from '~/alert';
import { __, s__ } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
import inboundAddProjectCIJobTokenScopeMutation from '../graphql/mutations/inbound_add_project_ci_job_token_scope.mutation.graphql';
import { TYPENAME_GROUP } from '~/graphql_shared/constants';
import inboundAddGroupOrProjectCIJobTokenScope from '../graphql/mutations/inbound_add_group_or_project_ci_job_token_scope.mutation.graphql';
import inboundRemoveProjectCIJobTokenScopeMutation from '../graphql/mutations/inbound_remove_project_ci_job_token_scope.mutation.graphql';
import inboundRemoveGroupCIJobTokenScopeMutation from '../graphql/mutations/inbound_remove_group_ci_job_token_scope.mutation.graphql';
import inboundUpdateCIJobTokenScopeMutation from '../graphql/mutations/inbound_update_ci_job_token_scope.mutation.graphql';
import inboundGetCIJobTokenScopeQuery from '../graphql/queries/inbound_get_ci_job_token_scope.query.graphql';
import inboundGetProjectsWithCIJobTokenScopeQuery from '../graphql/queries/inbound_get_projects_with_ci_job_token_scope.query.graphql';
import TokenProjectsTable from './token_projects_table.vue';
import inboundGetGroupsAndProjectsWithCIJobTokenScopeQuery from '../graphql/queries/inbound_get_groups_and_projects_with_ci_job_token_scope.query.graphql';
import TokenAccessTable from './token_access_table.vue';
export default {
i18n: {
toggleLabelTitle: s__('CICD|Limit access %{italicStart}to%{italicEnd} this project'),
toggleHelpText: s__(
`CICD|Prevent access to this project from other project CI/CD job tokens, unless the other project is added to the allowlist. It is a security risk to disable this feature, because unauthorized projects might attempt to retrieve an active token and access the API. %{linkStart}Learn more%{linkEnd}.`,
),
cardHeaderTitle: s__(
'CICD|Allow CI job tokens from the following projects to access this project',
toggleDescription: s__(
`CICD|Allow access to this project from authorized groups or projects by adding them to the allowlist. It is a security risk to disable this feature, because unauthorized projects might attempt to retrieve an active token and access the API. %{linkStart}Learn more%{linkEnd}.`,
),
cardHeaderTitle: s__('CICD|Groups and projects with access'),
settingDisabledMessage: s__(
'CICD|Enable feature to limit job token access, so only the projects in this list can access this project with a CI/CD job token.',
'CICD|No access is currently allowed to this project. Enable feature to authorize access from groups or projects in the allowlist below.',
),
addProject: __('Add project'),
addGroupOrProject: __('Add group or project'),
add: __('Add'),
cancel: __('Cancel'),
addProjectPlaceholder: __('Paste project path (i.e. gitlab-org/gitlab)'),
addProjectPlaceholder: __(
'Paste group path (i.e. gitlab-org) or project path (i.e. gitlab-org/gitlab)',
),
projectsFetchError: __('There was a problem fetching the projects'),
scopeFetchError: __('There was a problem fetching the job token scope value'),
},
fields: [
{
key: 'project',
label: __('Project with access'),
thClass: 'gl-border-t-none!',
key: 'fullPath',
label: '',
},
{
key: 'actions',
label: '',
tdClass: 'gl-text-right',
thClass: 'gl-border-t-none!',
},
],
components: {
@ -61,7 +62,7 @@ export default {
GlLoadingIcon,
GlSprintf,
GlToggle,
TokenProjectsTable,
TokenAccessTable,
},
inject: {
fullPath: {
@ -83,15 +84,16 @@ export default {
createAlert({ message: this.$options.i18n.scopeFetchError });
},
},
projects: {
query: inboundGetProjectsWithCIJobTokenScopeQuery,
groupsAndProjectsWithAccess: {
query: inboundGetGroupsAndProjectsWithCIJobTokenScopeQuery,
variables() {
return {
fullPath: this.fullPath,
};
},
update({ project }) {
return project?.ciJobTokenScope?.inboundAllowlist?.nodes ?? [];
this.projects = project?.ciJobTokenScope?.inboundAllowlist?.nodes ?? [];
this.groups = project?.ciJobTokenScope?.groupsAllowlist?.nodes ?? [];
},
error() {
createAlert({ message: this.$options.i18n.projectsFetchError });
@ -101,14 +103,15 @@ export default {
data() {
return {
inboundJobTokenScopeEnabled: null,
targetProjectPath: '',
targetPath: '',
projects: [],
groups: [],
isAddFormVisible: false,
};
},
computed: {
isProjectPathEmpty() {
return this.targetProjectPath === '';
isTargetPathEmpty() {
return this.targetPath === '';
},
ciJobTokenHelpPage() {
return helpPagePath('ci/jobs/ci_job_token#allow-access-to-your-project-with-a-job-token');
@ -139,17 +142,17 @@ export default {
createAlert({ message: error.message });
}
},
async addProject() {
async addGroupOrProject() {
try {
const {
data: {
ciJobTokenScopeAddProject: { errors },
ciJobTokenScopeAddGroupOrProject: { errors },
},
} = await this.$apollo.mutate({
mutation: inboundAddProjectCIJobTokenScopeMutation,
mutation: inboundAddGroupOrProjectCIJobTokenScope,
variables: {
projectPath: this.fullPath,
targetProjectPath: this.targetProjectPath,
targetPath: this.targetPath,
},
});
@ -159,23 +162,38 @@ export default {
} catch (error) {
createAlert({ message: error.message });
} finally {
this.clearTargetProjectPath();
this.getProjects();
this.clearTargetPath();
this.getGroupsAndProjects();
}
},
async removeProject(removeTargetPath) {
async removeItem(item) {
try {
const {
data: {
ciJobTokenScopeRemoveProject: { errors },
},
} = await this.$apollo.mutate({
mutation: inboundRemoveProjectCIJobTokenScopeMutation,
variables: {
projectPath: this.fullPath,
targetProjectPath: removeTargetPath,
},
});
let errors;
// eslint-disable-next-line no-underscore-dangle
if (item.__typename === TYPENAME_GROUP) {
const {
data: { ciJobTokenScopeRemoveGroup },
} = await this.$apollo.mutate({
mutation: inboundRemoveGroupCIJobTokenScopeMutation,
variables: {
projectPath: this.fullPath,
targetGroupPath: item.fullPath,
},
});
errors = ciJobTokenScopeRemoveGroup.errors;
} else {
const {
data: { ciJobTokenScopeRemoveProject },
} = await this.$apollo.mutate({
mutation: inboundRemoveProjectCIJobTokenScopeMutation,
variables: {
projectPath: this.fullPath,
targetProjectPath: item.fullPath,
},
});
errors = ciJobTokenScopeRemoveProject.errors;
}
if (errors.length) {
throw new Error(errors[0]);
@ -183,15 +201,15 @@ export default {
} catch (error) {
createAlert({ message: error.message });
} finally {
this.getProjects();
this.getGroupsAndProjects();
}
},
clearTargetProjectPath() {
this.targetProjectPath = '';
clearTargetPath() {
this.targetPath = '';
this.isAddFormVisible = false;
},
getProjects() {
this.$apollo.queries.projects.refetch();
getGroupsAndProjects() {
this.$apollo.queries.groupsAndProjectsWithAccess.refetch();
},
showAddForm() {
this.isAddFormVisible = true;
@ -206,6 +224,7 @@ export default {
<gl-toggle
v-model="inboundJobTokenScopeEnabled"
:label="$options.i18n.toggleLabelTitle"
class="gl-mt-5"
@change="updateCIJobTokenScope"
>
<template #label>
@ -215,8 +234,8 @@ export default {
</template>
</gl-sprintf>
</template>
<template #help>
<gl-sprintf :message="$options.i18n.toggleHelpText">
<template #description>
<gl-sprintf :message="$options.i18n.toggleDescription" class="gl-text-secondary">
<template #link="{ content }">
<gl-link :href="ciJobTokenHelpPage" class="inline-link" target="_blank">{{
content
@ -226,15 +245,29 @@ export default {
</template>
</gl-toggle>
<gl-alert
v-if="!inboundJobTokenScopeEnabled"
variant="warning"
class="gl-mt-6"
:dismissible="false"
:show-icon="false"
>
{{ $options.i18n.settingDisabledMessage }}
</gl-alert>
<div>
<gl-card
class="gl-new-card"
header-class="gl-new-card-header"
header-class="gl-new-card-header gl-border-bottom-0"
body-class="gl-new-card-body gl-px-0"
>
<template #header>
<div class="gl-new-card-title-wrapper">
<h5 class="gl-new-card-title">{{ $options.i18n.cardHeaderTitle }}</h5>
<span class="gl-new-card-count">
<gl-icon name="group" class="gl-mr-2" />
{{ groups.length }}
</span>
<span class="gl-new-card-count">
<gl-icon name="project" class="gl-mr-2" />
{{ projects.length }}
@ -246,46 +279,44 @@ export default {
size="small"
data-testid="toggle-form-btn"
@click="showAddForm"
>{{ $options.i18n.addProject }}</gl-button
>{{ $options.i18n.addGroupOrProject }}</gl-button
>
</div>
</template>
<div v-if="isAddFormVisible" class="gl-new-card-add-form gl-m-3">
<h4 class="gl-mt-0">{{ $options.i18n.addProject }}</h4>
<h4 class="gl-mt-0">{{ $options.i18n.addGroupOrProject }}</h4>
<gl-form-input
v-model="targetProjectPath"
v-model="targetPath"
:placeholder="$options.i18n.addProjectPlaceholder"
/>
<div class="gl-display-flex gl-mt-5">
<gl-button
variant="confirm"
:disabled="isProjectPathEmpty"
:disabled="isTargetPathEmpty"
class="gl-mr-3"
data-testid="add-project-btn"
@click="addProject"
@click="addGroupOrProject"
>
{{ $options.i18n.addProject }}
{{ $options.i18n.add }}
</gl-button>
<gl-button @click="clearTargetProjectPath">{{ $options.i18n.cancel }}</gl-button>
<gl-button @click="clearTargetPath">{{ $options.i18n.cancel }}</gl-button>
</div>
</div>
<token-projects-table
:projects="projects"
<token-access-table
:is-group="true"
:items="groups"
:table-fields="$options.fields"
@removeProject="removeProject"
@removeItem="removeItem"
/>
<token-access-table
:items="projects"
:table-fields="$options.fields"
@removeItem="removeItem"
/>
</gl-card>
<gl-alert
v-if="!inboundJobTokenScopeEnabled"
class="gl-mb-3"
variant="warning"
:dismissible="false"
:show-icon="false"
>
{{ $options.i18n.settingDisabledMessage }}
</gl-alert>
</div>
</template>
</div>

View File

@ -18,7 +18,7 @@ import removeProjectCIJobTokenScopeMutation from '../graphql/mutations/remove_pr
import updateCIJobTokenScopeMutation from '../graphql/mutations/update_ci_job_token_scope.mutation.graphql';
import getCIJobTokenScopeQuery from '../graphql/queries/get_ci_job_token_scope.query.graphql';
import getProjectsWithCIJobTokenScopeQuery from '../graphql/queries/get_projects_with_ci_job_token_scope.query.graphql';
import TokenProjectsTable from './token_projects_table.vue';
import TokenAccessTable from './token_access_table.vue';
// Note: This component will be removed in 17.0, as the outbound access token is getting deprecated
export default {
@ -48,15 +48,13 @@ export default {
}),
fields: [
{
key: 'project',
key: 'fullPath',
label: __('Project that can be accessed'),
thClass: 'gl-border-t-none!',
},
{
key: 'actions',
label: '',
tdClass: 'gl-text-right',
thClass: 'gl-border-t-none!',
},
],
components: {
@ -68,7 +66,7 @@ export default {
GlLoadingIcon,
GlSprintf,
GlToggle,
TokenProjectsTable,
TokenAccessTable,
},
mixins: [glFeatureFlagMixin()],
inject: {
@ -174,7 +172,7 @@ export default {
this.getProjects();
}
},
async removeProject(removeTargetPath) {
async removeProject(project) {
try {
const {
data: {
@ -185,7 +183,7 @@ export default {
variables: {
input: {
projectPath: this.fullPath,
targetProjectPath: removeTargetPath,
targetProjectPath: project.fullPath,
},
},
});
@ -282,10 +280,10 @@ export default {
<gl-button size="small" disabled>{{ $options.i18n.addProject }}</gl-button>
</div>
</template>
<token-projects-table
:projects="projects"
<token-access-table
:items="projects"
:table-fields="$options.fields"
@removeProject="removeProject"
@removeItem="removeProject"
/>
</gl-card>
</div>

View File

@ -0,0 +1,67 @@
<script>
import { GlButton, GlTableLite } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale';
export default {
components: {
GlButton,
GlTableLite,
},
inject: {
fullPath: {
default: '',
},
},
props: {
isGroup: {
type: Boolean,
required: false,
default: false,
},
items: {
type: Array,
required: true,
},
tableFields: {
type: Array,
required: true,
},
},
computed: {
emptyText() {
return sprintf(s__('CI/CD|No %{itemType}s have been added to the scope'), {
itemType: this.itemType,
});
},
itemType() {
return this.isGroup ? 'group' : 'project';
},
},
};
</script>
<template>
<gl-table-lite
:items="items"
:fields="tableFields"
:tbody-tr-attr="{ 'data-testid': `${itemType}s-token-table-row` }"
:empty-text="emptyText"
show-empty
stacked="sm"
thead-class="gl-display-none"
fixed
>
<template #cell(fullPath)="{ item }">
<span :data-testid="`token-access-${itemType}-name`">{{ item.fullPath }}</span>
</template>
<template #cell(actions)="{ item }">
<gl-button
v-if="item.fullPath !== fullPath"
category="primary"
icon="remove"
:aria-label="__('Remove access')"
@click="$emit('removeItem', item)"
/>
</template>
</gl-table-lite>
</template>

View File

@ -1,64 +0,0 @@
<script>
import { GlButton, GlTable } from '@gitlab/ui';
import { s__ } from '~/locale';
export default {
i18n: {
emptyText: s__('CI/CD|No projects have been added to the scope'),
},
components: {
GlButton,
GlTable,
},
inject: {
fullPath: {
default: '',
},
},
props: {
projects: {
type: Array,
required: true,
},
tableFields: {
type: Array,
required: true,
},
},
methods: {
removeProject(project) {
this.$emit('removeProject', project);
},
},
};
</script>
<template>
<gl-table
:items="projects"
:fields="tableFields"
:tbody-tr-attr="{ 'data-testid': 'projects-token-table-row' }"
:empty-text="$options.i18n.emptyText"
show-empty
stacked="sm"
fixed
>
<template #table-colgroup="{ fields }">
<col v-for="field in fields" :key="field.key" :class="field.columnClass" />
</template>
<template #cell(project)="{ item }">
<span data-testid="token-access-project-name">{{ item.fullPath }}</span>
</template>
<template #cell(actions)="{ item }">
<gl-button
v-if="item.fullPath !== fullPath"
category="primary"
variant="danger"
icon="remove"
:aria-label="__('Remove access')"
@click="removeProject(item.fullPath)"
/>
</template>
</gl-table>
</template>

View File

@ -0,0 +1,5 @@
mutation inboundAddGroupOrProjectCIJobTokenScope($projectPath: ID!, $targetPath: ID!) {
ciJobTokenScopeAddGroupOrProject(input: { projectPath: $projectPath, targetPath: $targetPath }) {
errors
}
}

View File

@ -1,7 +0,0 @@
mutation inboundAddProjectCIJobTokenScope($projectPath: ID!, $targetProjectPath: ID!) {
ciJobTokenScopeAddProject(
input: { projectPath: $projectPath, targetProjectPath: $targetProjectPath, direction: INBOUND }
) {
errors
}
}

View File

@ -0,0 +1,7 @@
mutation inboundRemoveGroupCIJobTokenScope($projectPath: ID!, $targetGroupPath: ID!) {
ciJobTokenScopeRemoveGroup(
input: { projectPath: $projectPath, targetGroupPath: $targetGroupPath }
) {
errors
}
}

View File

@ -1,15 +1,18 @@
query inboundGetProjectsWithCIJobTokenScope($fullPath: ID!) {
query inboundGetGroupsAndProjectsWithCIJobTokenScope($fullPath: ID!) {
project(fullPath: $fullPath) {
id
ciJobTokenScope {
groupsAllowlist {
nodes {
id
name
fullPath
}
}
inboundAllowlist {
nodes {
id
name
namespace {
id
fullPath
}
fullPath
}
}

View File

@ -76,7 +76,7 @@ export default {
userId: s__('UserProfile|Copy user ID: %{id}'),
userIdCopied: s__('UserProfile|User ID copied to clipboard'),
rssSubscribe: s__('UserProfile|Subscribe'),
reportToAdmin: s__('ReportAbuse|Report abuse to administrator'),
reportToAdmin: s__('ReportAbuse|Report abuse'),
},
};
</script>

View File

@ -32,7 +32,7 @@ export default {
<template>
<div
class="gl-display-flex gl-align-items-top gl-font-monospace gl-font-sm gl-word-break-all"
class="gl-display-flex gl-align-items-top gl-font-monospace gl-font-sm gl-break-all"
:class="[padding, borderClass]"
>
<div v-if="icon" class="gl-w-5 gl-mr-4">

View File

@ -265,10 +265,7 @@ export default {
<span class="gl-sr-only">{{ issuable.title }}</span>
</gl-form-checkbox>
<div class="issuable-main-info">
<div
data-testid="issuable-title"
class="issue-title title gl-display-flex gl-align-items-center"
>
<div data-testid="issuable-title" class="issue-title title gl-font-size-0">
<work-item-type-icon
v-if="showWorkItemTypeIcon"
class="gl-mr-2"
@ -281,6 +278,7 @@ export default {
name="eye-slash"
:title="__('Confidential')"
:aria-label="__('Confidential')"
class="gl-mr-2"
/>
<gl-icon
v-if="issuable.hidden"
@ -290,7 +288,7 @@ export default {
:aria-label="__('Hidden')"
/>
<gl-link
class="issue-title-text"
class="issue-title-text gl-font-base"
dir="auto"
:href="issuableLinkHref"
data-testid="issuable-title-link"

View File

@ -20,7 +20,7 @@ export default {
copyLinkText: __('Copy link'),
assignUserText: __('Assign to commenting user'),
unassignUserText: __('Unassign from commenting user'),
reportAbuseText: __('Report abuse to administrator'),
reportAbuseText: __('Report abuse'),
},
components: {
EmojiPicker: () => import('~/emoji/components/picker.vue'),

View File

@ -42,8 +42,8 @@ class Projects::BranchesController < Projects::ApplicationController
render status: :service_unavailable
end
format.json do
branches = BranchesFinder.new(@repository, params).execute
branches = Kaminari.paginate_array(branches).page(params[:page])
branches = BranchesFinder.new(@repository, branches_params).execute
branches = Kaminari.paginate_array(branches).page(branches_params[:page])
render json: branches.map(&:name)
end
end
@ -135,7 +135,7 @@ class Projects::BranchesController < Projects::ApplicationController
private
def sort_param
sort = params[:sort].presence
sort = branches_params[:sort].presence
unless sort.in?(supported_sort_options)
flash.now[:alert] = _("Unsupported sort value.")
@ -191,7 +191,7 @@ class Projects::BranchesController < Projects::ApplicationController
def redirect_for_legacy_index_sort_or_search
# Normalize a legacy URL with redirect
if request.format != :json && !params[:state].presence && [:sort, :search, :page].any? { |key| params[key].presence }
if request.format != :json && !branches_params[:state].presence && [:sort, :search, :page].any? { |key| branches_params[key].presence }
redirect_to project_branches_filtered_path(@project, state: 'all'), notice: _('Update your bookmarked URLs as filtered/sorted branches URL has been changed.')
end
end
@ -200,7 +200,7 @@ class Projects::BranchesController < Projects::ApplicationController
return fetch_branches_for_overview if @mode == 'overview'
@branches, @prev_path, @next_path =
Projects::BranchesByModeService.new(@project, params.merge(sort: @sort, mode: @mode)).execute
Projects::BranchesByModeService.new(@project, branches_params.merge(sort: @sort, mode: @mode)).execute
end
def fetch_merge_requests_for_branches
@ -227,7 +227,7 @@ class Projects::BranchesController < Projects::ApplicationController
end
def fetch_mode
state = params[:state].presence
state = branches_params[:state].presence
return 'overview' unless state
@ -243,4 +243,8 @@ class Projects::BranchesController < Projects::ApplicationController
confidential_issue_project
end
def branches_params
params.permit(:page, :state, :sort, :search, :page_token, :offset)
end
end

View File

@ -7,7 +7,7 @@ class Projects::NotesController < Projects::ApplicationController
include NotesHelper
include ToggleAwardEmoji
before_action :disable_query_limiting, only: [:create, :update]
before_action :disable_query_limiting, only: [:create, :update, :index]
before_action :authorize_read_note!
before_action :authorize_create_note!, only: [:create]
before_action :authorize_resolve_note!, only: [:resolve, :unresolve]
@ -118,6 +118,8 @@ class Projects::NotesController < Projects::ApplicationController
end
def disable_query_limiting
# Using a single disable! Call to not repeat this code, but there are different issues created for each action
# index: https://gitlab.com/gitlab-org/gitlab/-/issues/460923
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20800')
end
end

View File

@ -4,10 +4,11 @@ module Issues
class ConfidentialityFilter < Issuables::BaseFilter
CONFIDENTIAL_ACCESS_LEVEL = Gitlab::Access::REPORTER
def initialize(current_user:, parent:, assignee_filter:, **kwargs)
def initialize(current_user:, parent:, assignee_filter:, related_groups: nil, **kwargs)
@current_user = current_user
@parent = parent
@assignee_filter = assignee_filter
@related_groups = related_groups
super(**kwargs)
end
@ -24,7 +25,7 @@ module Issues
issues.confidential_only.merge(
issues.authored(@current_user)
.or(issues.assigned_to(@current_user))
.or(access_to_project_exists(issues))
.or(access_to_parent_exists(issues))
)
).allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/422045')
end
@ -41,13 +42,23 @@ module Issues
@assignee_filter.includes_user?(@current_user)
end
def access_to_project_exists(issues)
issues.where_exists(
def access_to_parent_exists(issues)
access_to_project_level_issue_exists = issues.where_exists(
@current_user.authorizations_for_projects(
min_access_level: CONFIDENTIAL_ACCESS_LEVEL,
related_project_column: 'issues.project_id'
)
)
return access_to_project_level_issue_exists if @related_groups.nil?
access_to_project_level_issue_exists.project_level.or(
issues.group_level.in_namespaces(
Group.id_in(
Group.groups_user_can(@related_groups, @current_user, :read_confidential_issues, same_root: true)
)
)
)
end
end
end

View File

@ -67,16 +67,6 @@ class IssuesFinder < IssuableFinder
super.with_projects_matching_search_data
end
def group_namespaces
return if params[:project_id] || params[:projects]
Group.id_in(params.group).select(:id)
end
def project_namespaces
params.projects.select(:project_namespace_id)
end
def by_confidential(items)
Issues::ConfidentialityFilter.new(
current_user: current_user,

View File

@ -6,6 +6,8 @@
# WorkItems instead of Issues
module WorkItems
class WorkItemsFinder < IssuesFinder
include Gitlab::Utils::StrongMemoize
def klass
WorkItem
end
@ -47,12 +49,27 @@ module WorkItems
super
end
override :by_confidential
def by_confidential(items)
return super unless include_namespace_level_work_items?
Issues::ConfidentialityFilter.new(
current_user: current_user,
params: original_params,
parent: root_ancestor_group,
assignee_filter: assignee_filter,
related_groups: related_groups
).filter(items)
end
override :by_parent
def by_parent(items)
return super unless include_namespace_level_work_items?
relations = [group_namespaces, project_namespaces].compact
return items.none if relations.empty?
namespaces = if relations.one?
relations.first
else
@ -62,8 +79,68 @@ module WorkItems
items.in_namespaces(namespaces)
end
def group_namespaces
return if params[:project_id] || params[:projects]
related_groups_with_access.select(:id)
end
def related_groups_with_access
# If the user is not signed in, we just return public groups
return related_groups.public_to_user unless current_user
# If the user is an admin or a member of the root group, they will have read access to all
# work items in the subgroups so we can skip the expensive permissions check
if Ability.allowed?(current_user, :read_all_resources) || root_ancestor_group.member?(current_user)
return related_groups
end
Group.id_in(
Group.groups_user_can(related_groups, current_user, :read_work_item, same_root: true)
)
end
def related_groups
if include_ancestors? && include_descendants?
params.group.self_and_hierarchy
elsif include_ancestors?
params.group.self_and_ancestors
elsif include_descendants?
params.group.self_and_descendants
else
Group.id_in(params.group.id)
end
end
strong_memoize_attr :related_groups
def root_ancestor_group
include_ancestors? ? params.group.root_ancestor : params.group
end
def project_namespaces
return unless include_descendants?
projects = Project.in_namespace(params.group.self_and_descendant_ids)
projects = projects.id_in(params[:projects]) if params[:projects]
projects
.public_or_visible_to_user(current_user, ProjectFeature.required_minimum_access_level(klass.base_class))
.with_feature_available_for_user(klass.base_class, current_user)
.select(:project_namespace_id)
end
def include_namespace_level_work_items?
params.group? && Feature.enabled?(:namespace_level_work_items, params.group)
end
def include_descendants?
params.fetch(:include_descendants, false)
end
strong_memoize_attr :include_descendants?
def include_ancestors?
params.fetch(:include_ancestors, false)
end
strong_memoize_attr :include_ancestors?
end
end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
module Mutations
module Ci
module Runner
module Cache
class Clear < BaseMutation
graphql_name 'RunnerCacheClear'
authorize :admin_pipeline
argument :project_id, ::Types::GlobalIDType[Project],
required: true,
description: 'Global ID of the project that will have its runner cache cleared.'
def resolve(project_id:)
project = authorized_find!(id: project_id)
ResetProjectCacheService.new(project, current_user).execute
end
end
end
end
end
end

View File

@ -4,6 +4,16 @@ module Resolvers
module Namespaces
# rubocop:disable Graphql/ResolverType -- inherited from Resolvers::WorkItemsResolver
class WorkItemsResolver < ::Resolvers::WorkItemsResolver
argument :include_ancestors, GraphQL::Types::Boolean,
required: false,
default_value: false,
description: 'Include work items from ancestor groups.'
argument :include_descendants, GraphQL::Types::Boolean,
required: false,
default_value: false,
description: 'Include work items from descendant groups and projects.'
def ready?(**args)
return false if Feature.disabled?(:namespace_level_work_items, resource_parent)

View File

@ -39,6 +39,8 @@ module Types
description: 'Components belonging to the catalog resource.',
alpha: { milestone: '16.7' }
# TODO: Turn this into a proper markup_field, which will require some refactoring.
# https://gitlab.com/gitlab-org/gitlab/-/issues/460462
field :readme_html, GraphQL::Types::String, null: true, calls_gitaly: true,
description: 'GitLab Flavored Markdown rendering of README.md. This field ' \
'can only be resolved for one version in any single request.',
@ -51,10 +53,8 @@ module Types
end
def readme_html
return unless Ability.allowed?(current_user, :read_code, object.project)
markdown_context = context.to_h.dup.merge(project: object.project)
::MarkupHelper.markdown(object.readme&.data, markdown_context)
ctx = context.to_h.dup.merge(project: object.project)
::MarkupHelper.markdown(object.readme.data, ctx, { requested_path: object.readme.path })
end
end
# rubocop: enable Graphql/AuthorizeTypes

View File

@ -171,6 +171,7 @@ module Types
mount_mutation Mutations::Ci::PipelineTrigger::Update, alpha: { milestone: '16.3' }
mount_mutation Mutations::Ci::ProjectCiCdSettingsUpdate
mount_mutation Mutations::Ci::Runner::BulkDelete, alpha: { milestone: '15.3' }
mount_mutation Mutations::Ci::Runner::Cache::Clear
mount_mutation Mutations::Ci::Runner::Create, alpha: { milestone: '15.10' }
mount_mutation Mutations::Ci::Runner::Delete
mount_mutation Mutations::Ci::Runner::Update

View File

@ -21,7 +21,7 @@ module Types
end
permission_field :can_merge, calls_gitaly: true
permission_field :can_approve
permission_field :can_approve, calls_gitaly: true
def can_merge
object.can_be_merged_by?(context[:current_user])

View File

@ -83,13 +83,14 @@ module MarkupHelper
render_links(text)
end
def markdown(text, context = {})
def markdown(text, context = {}, postprocess = {})
return '' unless text.present?
context[:project] ||= @project
context[:group] ||= @group
html = Markup::RenderingService.new(text, context: context, postprocess_context: postprocess_context).execute
html = Markup::RenderingService.new(text, context: context,
postprocess_context: postprocess_context.merge!(postprocess)).execute
Hamlit::RailsHelpers.preserve(html)
end

View File

@ -1160,23 +1160,7 @@ module Ci
end
def job_jwt_variables
if Feature.enabled?(:remove_shared_jwts) || id_tokens?
id_tokens_variables
else
predefined_jwt_variables
end
end
def predefined_jwt_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
jwt = Gitlab::Ci::Jwt.for_build(self)
jwt_v2 = Gitlab::Ci::JwtV2.for_build(self)
variables.append(key: 'CI_JOB_JWT', value: jwt, public: false, masked: true)
variables.append(key: 'CI_JOB_JWT_V1', value: jwt, public: false, masked: true)
variables.append(key: 'CI_JOB_JWT_V2', value: jwt_v2, public: false, masked: true)
rescue OpenSSL::PKey::RSAError, Gitlab::Ci::Jwt::NoSigningKeyError => e
Gitlab::ErrorTracking.track_exception(e)
end
id_tokens_variables
end
def id_tokens_variables

View File

@ -177,12 +177,12 @@ class CommitCollection
def committer_emails_for(commits, include_author_when_signed: false)
commit_author_change_enabled = ::Feature.enabled?(:web_ui_commit_author_change, project)
if commit_author_change_enabled
if include_author_when_signed && commit_author_change_enabled
commits.each(&:signature) # preload signatures
end
commits.filter_map do |commit|
if commit_author_change_enabled && commit.signature&.verified_system? && include_author_when_signed
if include_author_when_signed && commit_author_change_enabled && commit.signature&.verified_system?
commit.author_email
else
commit.committer_email

View File

@ -124,6 +124,9 @@ class Issue < ApplicationRecord
pg_full_text_searchable columns: [{ name: 'title', weight: 'A' }, { name: 'description', weight: 'B' }]
scope :project_level, -> { where.not(project_id: nil) }
scope :group_level, -> { where(project_id: nil) }
scope :in_namespaces, ->(namespaces) { where(namespace: namespaces) }
scope :in_projects, ->(project_ids) { where(project_id: project_ids) }
scope :not_in_projects, ->(project_ids) { where.not(project_id: project_ids) }

View File

@ -176,6 +176,7 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
enable :read_custom_emoji
enable :read_counts
enable :read_issue
enable :read_work_item
enable :read_namespace
end

View File

@ -3,7 +3,7 @@
.js-table-contents
= blob_icon blob.mode, blob.name
%strong.file-title-name.gl-word-break-all{ data: { testid: 'file-name-content' } }
%strong.file-title-name.gl-break-all{ data: { testid: 'file-name-content' } }
= blob.name
= copy_file_path_button(blob.path)

View File

@ -17,13 +17,13 @@
- if diff_file.renamed_file?
- old_path, new_path = mark_inline_diffs(diff_file.old_path, diff_file.new_path)
%strong.file-title-name.has-tooltip.gl-word-break-all{ data: { title: diff_file.old_path, container: 'body' } }
%strong.file-title-name.has-tooltip.gl-break-all{ data: { title: diff_file.old_path, container: 'body' } }
= old_path
&rarr;
%strong.file-title-name.has-tooltip.gl-word-break-all{ data: { title: diff_file.new_path, container: 'body' } }
%strong.file-title-name.has-tooltip.gl-break-all{ data: { title: diff_file.new_path, container: 'body' } }
= new_path
- else
%strong.file-title-name.has-tooltip.gl-word-break-all{ data: { title: diff_file.file_path, container: 'body', testid: 'file-name-content' } }
%strong.file-title-name.has-tooltip.gl-break-all{ data: { title: diff_file.file_path, container: 'body', testid: 'file-name-content' } }
= diff_file.file_path
- if diff_file.deleted_file?

View File

@ -9,7 +9,7 @@
.well-segment
.gl-justify-content-left.gl-display-flex.gl-align-items-center
= render Pajamas::AvatarComponent.new(current_user, size: 24, avatar_options: { data: { qa_selector: 'user_avatar_content' }, title: current_user.username })
.gl-pl-4.gl-word-break-all
.gl-pl-4.gl-break-all
%span= _('Signed in as %{username}') % { username: '@' + current_user.username }
= gitlab_ui_form_for @user, url: user_settings_password_path, method: :post do |f|

View File

@ -14,7 +14,11 @@ module Gitlab
importer.execute
ImportUsersWorker.perform_async(project.id)
if Feature.enabled?(:bitbucket_server_convert_mentions_to_users, project.creator)
ImportUsersWorker.perform_async(project.id)
else
ImportPullRequestsWorker.perform_async(project.id)
end
end
def importer_class

View File

@ -5,5 +5,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/146878
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/448342
milestone: '16.10'
group: group::source code
type: gitlab_com_derisk
default_enabled: false
type: beta
default_enabled: true

View File

@ -5,5 +5,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/148889
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/455288
milestone: '16.11'
group: group::source code
type: gitlab_com_derisk
default_enabled: false
type: beta
default_enabled: true

View File

@ -0,0 +1,8 @@
---
name: bitbucket_server_convert_mentions_to_users
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139097
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/434453
milestone: '16.7'
type: development
group: group::import and integrate
default_enabled: false

View File

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350460
milestone: '13.8'
type: development
group: group::respond
default_enabled: false
default_enabled: true

View File

@ -1,9 +0,0 @@
---
name: deprecate_unified_approval_rules
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/388191
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/150182
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/457258
milestone: '17.0'
group: group::environments
type: gitlab_com_derisk
default_enabled: false

View File

@ -1,9 +0,0 @@
---
name: remove_shared_jwts
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/423106
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/148106
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/452294
milestone: '16.11'
group: group::pipeline security
type: gitlab_com_derisk
default_enabled: false

View File

@ -1,6 +1,6 @@
- title: "Breaking change to the Maven repository group permissions"
announcement_milestone: "16.6"
removal_milestone: "17.0"
removal_milestone: "18.0"
breaking_change: true
reporter: trizzi
stage: Package
@ -9,7 +9,7 @@
The Maven repository exposes an API endpoint at the group level that allows Maven clients to download files from a specific package. The package finder first locates the package within the group, and then finds the file within the package.
However, there is a limitation that affects duplicate package names hosted in different projects. The Maven package finder always returns the most recent package, but the "most recent" filter depends on user permissions. It is possible for a user with different permissions in different projects to download the wrong Maven package.
In GitLab 17.0, the package finder logic will be fixed so that the "most recent" package is the last updated name and version of a package in a group. User permissions will be checked after the most recent package is found.
In GitLab 18.0, the package finder logic will be fixed so that the "most recent" package is the last updated name and version of a package in a group. User permissions will be checked after the most recent package is found.
After the change, download requests for users without correct permissions will be rejected. If your workflow depends on the current bugged behavior, this fix will introduce a breaking change.
The change will be introduced in GitLab 16.6 behind a feature flag. If you are interested in enabling the feature flag for your group, leave a comment in [issue 393933](https://gitlab.com/gitlab-org/gitlab/-/issues/393933).

View File

@ -1,6 +1,6 @@
- title: "Dependency Proxy: Access tokens to have additional scope checks"
announcement_milestone: "16.7"
removal_milestone: "17.0"
removal_milestone: "18.0"
breaking_change: true
reporter: trizzi
stage: Package
@ -8,6 +8,6 @@
body: |
When using the Dependency Proxy for containers with a group access token or personal access token, `docker login` and `docker pull` requests with insufficient scopes for Dependency Proxy are not rejected.
GitLab 17.0 adds checks for group or personal access tokens authenticating with the dependency proxy for containers. This is a breaking change, because tokens without the required scopes will fail.
GitLab 18.0 adds checks for group or personal access tokens authenticating with the dependency proxy for containers. This is a breaking change, because tokens without the required scopes will fail.
To help avoid being impacted by this breaking change, create new access tokens with the [required scopes](https://docs.gitlab.com/ee/user/packages/dependency_proxy/#authenticate-with-the-dependency-proxy), and update your workflow variables and scripts with those new tokens.

View File

@ -8,6 +8,8 @@ description: Used to replicate storage attachments migration paths on Geo second
from regular to hashed storage.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3544
milestone: '10.3'
removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151938
removed_in_milestone: '17.0'
gitlab_schema: gitlab_main_cell
allow_cross_joins:
- gitlab_main_clusterwide

View File

@ -8,6 +8,8 @@ description: Used to replicate repository migration paths on Geo secondaries fro
regular to hashed storage.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3066
milestone: '10.2'
removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151938
removed_in_milestone: '17.0'
gitlab_schema: gitlab_main_cell
allow_cross_joins:
- gitlab_main_clusterwide

View File

@ -7,6 +7,8 @@ feature_categories:
description: Geo event for when a repository gets created, belongs to geo_event_log.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/f3eacf881659b7af97b7c7ba3289237ec6cdc1cb
milestone: '10.0'
removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151938
removed_in_milestone: '17.0'
gitlab_schema: gitlab_main_cell
allow_cross_joins:
- gitlab_main_clusterwide

View File

@ -7,6 +7,8 @@ feature_categories:
description: Geo event for when a repository gets deleted, belongs to geo_event_log.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/04c3da24ac5975b140cf2e6a7e33414543f148f5
milestone: '9.4'
removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151938
removed_in_milestone: '17.0'
gitlab_schema: gitlab_main_cell
allow_cross_joins:
- gitlab_main_clusterwide

View File

@ -7,6 +7,8 @@ feature_categories:
description: Geo event for when a repository gets renamed, belongs to geo_event_log.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/6e5fa040d1c689fad4e110dd10be8ddba61ea7ef
milestone: '9.4'
removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151938
removed_in_milestone: '17.0'
gitlab_schema: gitlab_main_cell
allow_cross_joins:
- gitlab_main_clusterwide

View File

@ -8,6 +8,8 @@ description: Geo event for when a repository gets updated (content changed), bel
to geo_event_log.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/71cc57b1e4b7721c93107357517235a18f7ba8e2
milestone: '9.3'
removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151938
removed_in_milestone: '17.0'
gitlab_schema: gitlab_main_cell
allow_cross_joins:
- gitlab_main_clusterwide

View File

@ -8,6 +8,8 @@ description: Geo event for when a project gets reverified on the primary, belong
to geo_event_log.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/7394
milestone: '11.4'
removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151938
removed_in_milestone: '17.0'
gitlab_schema: gitlab_main_cell
allow_cross_joins:
- gitlab_main_clusterwide

View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
class RemoveForeignKeyGeoHashedStorageMigratedEvents < Gitlab::Database::Migration[2.2]
milestone '17.0'
disable_ddl_transaction!
FROM_TABLE = :geo_hashed_storage_migrated_events
TO_TABLE = :projects
def up
with_lock_retries do
remove_foreign_key(
FROM_TABLE,
TO_TABLE,
column: :project_id,
if_exists: true
)
end
end
def down
add_concurrent_foreign_key(
FROM_TABLE,
TO_TABLE,
name: :fk_rails_687ed7d7c5,
column: :project_id,
on_delete: :cascade
)
end
end

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
class DropTableGeoHashedStorageMigratedEvents < Gitlab::Database::Migration[2.2]
milestone '17.0'
def up
drop_table :geo_hashed_storage_migrated_events
end
def down
create_table :geo_hashed_storage_migrated_events do |t|
t.integer :project_id, index: { name: 'index_geo_hashed_storage_migrated_events_on_project_id' }, null: false
t.text :repository_storage_name, null: false
t.text :old_disk_path, null: false
t.text :new_disk_path, null: false
t.text :old_wiki_disk_path, null: false
t.text :new_wiki_disk_path, null: false
t.integer :old_storage_version, limit: 2
t.integer :new_storage_version, limit: 2, null: false
t.text :old_design_disk_path
t.text :new_design_disk_path
end
end
end

View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
class RemoveForeignKeyGeoHashedStorageAttachmentsEvents < Gitlab::Database::Migration[2.2]
milestone '17.0'
disable_ddl_transaction!
FROM_TABLE = :geo_hashed_storage_attachments_events
TO_TABLE = :projects
def up
with_lock_retries do
remove_foreign_key(
FROM_TABLE,
TO_TABLE,
column: :project_id,
if_exists: true
)
end
end
def down
add_concurrent_foreign_key(
FROM_TABLE,
TO_TABLE,
name: :fk_rails_d496b088e9,
column: :project_id,
on_delete: :cascade
)
end
end

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class DropTableGeoHashedStorageAttachementsEvents < Gitlab::Database::Migration[2.2]
milestone '17.0'
def up
drop_table :geo_hashed_storage_attachments_events
end
def down
create_table :geo_hashed_storage_attachments_events do |t|
t.integer :project_id, index: { name: 'index_geo_hashed_storage_attachments_events_on_project_id' }, null: false
t.text :old_attachments_path, null: false
t.text :new_attachments_path, null: false
end
end
end

View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
class RemoveForeignKeyGeoRepositoryUpdatedEvents < Gitlab::Database::Migration[2.2]
milestone '17.0'
disable_ddl_transaction!
FROM_TABLE = :geo_repository_updated_events
TO_TABLE = :projects
def up
with_lock_retries do
remove_foreign_key(
FROM_TABLE,
TO_TABLE,
column: :project_id,
if_exists: true
)
end
end
def down
add_concurrent_foreign_key(
FROM_TABLE,
TO_TABLE,
name: :fk_rails_2b70854c08,
column: :project_id,
on_delete: :cascade
)
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class DropTableGeoRepositoryUpdatedEvents < Gitlab::Database::Migration[2.2]
milestone '17.0'
def up
drop_table :geo_repository_updated_events
end
def down
create_table :geo_repository_updated_events do |t|
t.integer :branches_affected, null: false
t.integer :tags_affected, null: false
t.integer :project_id, index: { name: 'index_geo_repository_updated_events_on_project_id' }, null: false
t.integer :source, limit: 2, index: { name: 'index_geo_repository_updated_events_on_source' }, null: false
t.boolean :new_branch, default: false, null: false
t.boolean :remove_branch, default: false, null: false
t.text :ref
end
end
end

View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
class RemoveForeignKeyGeoRepositoryRenamedEvents < Gitlab::Database::Migration[2.2]
milestone '17.0'
disable_ddl_transaction!
FROM_TABLE = :geo_repository_renamed_events
TO_TABLE = :projects
def up
with_lock_retries do
remove_foreign_key(
FROM_TABLE,
TO_TABLE,
column: :project_id,
if_exists: true
)
end
end
def down
add_concurrent_foreign_key(
FROM_TABLE,
TO_TABLE,
name: :fk_rails_4e6524febb,
column: :project_id,
on_delete: :cascade
)
end
end

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
class DropTableGeoRepositoryRenamedEvents < Gitlab::Database::Migration[2.2]
milestone '17.0'
def up
drop_table :geo_repository_renamed_events
end
def down
create_table :geo_repository_renamed_events do |t|
t.integer :project_id, index: { name: 'index_geo_repository_renamed_events_on_project_id' }, null: false
t.text :repository_storage_name, null: false
t.text :old_path_with_namespace, null: false
t.text :new_path_with_namespace, null: false
t.text :old_wiki_path_with_namespace, null: false
t.text :new_wiki_path_with_namespace, null: false
t.text :old_path, null: false
t.text :new_path, null: false
end
end
end

View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
class RemoveForeignKeyGeoRepositoryCreatedEvents < Gitlab::Database::Migration[2.2]
milestone '17.0'
disable_ddl_transaction!
FROM_TABLE = :geo_repository_created_events
TO_TABLE = :projects
def up
with_lock_retries do
remove_foreign_key(
FROM_TABLE,
TO_TABLE,
column: :project_id,
if_exists: true
)
end
end
def down
add_concurrent_foreign_key(
FROM_TABLE,
TO_TABLE,
name: :fk_rails_1f49e46a61,
column: :project_id,
on_delete: :cascade
)
end
end

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
class DropTableGeoRepositoryCreatedEvents < Gitlab::Database::Migration[2.2]
milestone '17.0'
def up
drop_table :geo_repository_created_events
end
def down
create_table :geo_repository_created_events do |t|
t.integer :project_id, index: { name: 'index_geo_repository_created_events_on_project_id' }, null: false
t.text :repository_storage_name, null: false
t.text :repo_path, null: false
t.text :wiki_path
t.text :project_name, null: false
end
end
end

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
class DropTableGeoRepositoryDeletedEvents < Gitlab::Database::Migration[2.2]
milestone '17.0'
def up
drop_table :geo_repository_deleted_events
end
def down
create_table :geo_repository_deleted_events do |t|
t.integer :project_id, index: { name: 'index_geo_repository_deleted_events_on_project_id' }, null: false
t.text :repository_storage_name, null: false
t.text :deleted_path, null: false
t.text :deleted_wiki_path
t.text :deleted_project_name, null: false
end
end
end

View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
class RemoveForeignKeyGeoResetChecksumEvents < Gitlab::Database::Migration[2.2]
milestone '17.0'
disable_ddl_transaction!
FROM_TABLE = :geo_reset_checksum_events
TO_TABLE = :projects
def up
with_lock_retries do
remove_foreign_key(
FROM_TABLE,
TO_TABLE,
column: :project_id,
if_exists: true
)
end
end
def down
add_concurrent_foreign_key(
FROM_TABLE,
TO_TABLE,
name: :fk_rails_910a06f12b,
column: :project_id,
on_delete: :cascade
)
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class DropTableGeoResetChecksumEvents < Gitlab::Database::Migration[2.2]
milestone '17.0'
def up
drop_table :geo_reset_checksum_events
end
def down
create_table :geo_reset_checksum_events do |t|
t.integer :project_id, index: { name: 'index_geo_reset_checksum_events_on_project_id' }, null: false
end
end
end

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class AddIndexMembersOnLowerInviteEmail < Gitlab::Database::Migration[2.2]
milestone '17.0'
INDEX_NAME = 'index_members_on_lower_invite_email'
disable_ddl_transaction!
def up
add_concurrent_index :members, '(lower(invite_email))', name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :members, INDEX_NAME
end
end

View File

@ -0,0 +1 @@
58549bf920eb048c92b8faeeb1c09aabb99bfc6000366eef283bc3f04d15a1a4

View File

@ -0,0 +1 @@
d3e1f2dfccc5d329d677a2163d5a44df5c5bd575ee8c2ae678609de6d5eef942

View File

@ -0,0 +1 @@
7674c725ec40ca8f35f3fa34d2c0004d93e60d082143c7d95648d727ba1c1633

View File

@ -0,0 +1 @@
4438ab576df6ec912cf25576c52a096a0a625e8b5aee5a087f5d7db59504d164

View File

@ -0,0 +1 @@
9aa10a6889d0d25a080895406fd4a1d0182fe0ce258b435970fc48cfc3cd98cb

View File

@ -0,0 +1 @@
60e54814c34eaff5a4a002152be76b9c5fad5db0058d2fd1eb1ce8f80bcbac68

View File

@ -0,0 +1 @@
e8da0d1411165e4c37380cf6f5b3f1e6814889b4c5c7236235ea40ef198cafda

View File

@ -0,0 +1 @@
312533b34332d08f8bbf10430224996a7a33e16303b5352f4347943133349123

View File

@ -0,0 +1 @@
ca5f6d8e698550c3f3711cbcc2b52f4c17236ad065bf2b4abb703e9fff658770

View File

@ -0,0 +1 @@
d07a24c6ebe58b72d8d2dc95e9f4f00347ff35add81ad13bec8c40497f599f8b

View File

@ -0,0 +1 @@
824e428c16fde2be4c72dd1d426ec3dc1e8e10f6c72018df26a7eea6ab6eb26c

View File

@ -0,0 +1 @@
dabe889047ccac9b5b354f83bd8f910936aca26a968fe79fb1cdcab0fd618dd2

View File

@ -0,0 +1 @@
aafee50b23b182e7f8674086accdb7239e31c709bcc810d7a5fd88f455ea758b

View File

@ -0,0 +1 @@
2879df8b5e17741acdafa450706f7d93901e183c96a148136b9bc39d1d8a6833

View File

@ -9249,45 +9249,6 @@ CREATE SEQUENCE geo_events_id_seq
ALTER SEQUENCE geo_events_id_seq OWNED BY geo_events.id;
CREATE TABLE geo_hashed_storage_attachments_events (
id bigint NOT NULL,
project_id integer NOT NULL,
old_attachments_path text NOT NULL,
new_attachments_path text NOT NULL
);
CREATE SEQUENCE geo_hashed_storage_attachments_events_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE geo_hashed_storage_attachments_events_id_seq OWNED BY geo_hashed_storage_attachments_events.id;
CREATE TABLE geo_hashed_storage_migrated_events (
id bigint NOT NULL,
project_id integer NOT NULL,
repository_storage_name text NOT NULL,
old_disk_path text NOT NULL,
new_disk_path text NOT NULL,
old_wiki_disk_path text NOT NULL,
new_wiki_disk_path text NOT NULL,
old_storage_version smallint,
new_storage_version smallint NOT NULL,
old_design_disk_path text,
new_design_disk_path text
);
CREATE SEQUENCE geo_hashed_storage_migrated_events_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE geo_hashed_storage_migrated_events_id_seq OWNED BY geo_hashed_storage_migrated_events.id;
CREATE TABLE geo_node_namespace_links (
id integer NOT NULL,
geo_node_id integer NOT NULL,
@ -9383,97 +9344,6 @@ CREATE SEQUENCE geo_repositories_changed_events_id_seq
ALTER SEQUENCE geo_repositories_changed_events_id_seq OWNED BY geo_repositories_changed_events.id;
CREATE TABLE geo_repository_created_events (
id bigint NOT NULL,
project_id integer NOT NULL,
repository_storage_name text NOT NULL,
repo_path text NOT NULL,
wiki_path text,
project_name text NOT NULL
);
CREATE SEQUENCE geo_repository_created_events_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE geo_repository_created_events_id_seq OWNED BY geo_repository_created_events.id;
CREATE TABLE geo_repository_deleted_events (
id bigint NOT NULL,
project_id integer NOT NULL,
repository_storage_name text NOT NULL,
deleted_path text NOT NULL,
deleted_wiki_path text,
deleted_project_name text NOT NULL
);
CREATE SEQUENCE geo_repository_deleted_events_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE geo_repository_deleted_events_id_seq OWNED BY geo_repository_deleted_events.id;
CREATE TABLE geo_repository_renamed_events (
id bigint NOT NULL,
project_id integer NOT NULL,
repository_storage_name text NOT NULL,
old_path_with_namespace text NOT NULL,
new_path_with_namespace text NOT NULL,
old_wiki_path_with_namespace text NOT NULL,
new_wiki_path_with_namespace text NOT NULL,
old_path text NOT NULL,
new_path text NOT NULL
);
CREATE SEQUENCE geo_repository_renamed_events_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE geo_repository_renamed_events_id_seq OWNED BY geo_repository_renamed_events.id;
CREATE TABLE geo_repository_updated_events (
id bigint NOT NULL,
branches_affected integer NOT NULL,
tags_affected integer NOT NULL,
project_id integer NOT NULL,
source smallint NOT NULL,
new_branch boolean DEFAULT false NOT NULL,
remove_branch boolean DEFAULT false NOT NULL,
ref text
);
CREATE SEQUENCE geo_repository_updated_events_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE geo_repository_updated_events_id_seq OWNED BY geo_repository_updated_events.id;
CREATE TABLE geo_reset_checksum_events (
id bigint NOT NULL,
project_id integer NOT NULL
);
CREATE SEQUENCE geo_reset_checksum_events_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE geo_reset_checksum_events_id_seq OWNED BY geo_reset_checksum_events.id;
CREATE TABLE ghost_user_migrations (
id bigint NOT NULL,
user_id bigint NOT NULL,
@ -19400,10 +19270,6 @@ ALTER TABLE ONLY geo_event_log ALTER COLUMN id SET DEFAULT nextval('geo_event_lo
ALTER TABLE ONLY geo_events ALTER COLUMN id SET DEFAULT nextval('geo_events_id_seq'::regclass);
ALTER TABLE ONLY geo_hashed_storage_attachments_events ALTER COLUMN id SET DEFAULT nextval('geo_hashed_storage_attachments_events_id_seq'::regclass);
ALTER TABLE ONLY geo_hashed_storage_migrated_events ALTER COLUMN id SET DEFAULT nextval('geo_hashed_storage_migrated_events_id_seq'::regclass);
ALTER TABLE ONLY geo_node_namespace_links ALTER COLUMN id SET DEFAULT nextval('geo_node_namespace_links_id_seq'::regclass);
ALTER TABLE ONLY geo_node_statuses ALTER COLUMN id SET DEFAULT nextval('geo_node_statuses_id_seq'::regclass);
@ -19412,16 +19278,6 @@ ALTER TABLE ONLY geo_nodes ALTER COLUMN id SET DEFAULT nextval('geo_nodes_id_seq
ALTER TABLE ONLY geo_repositories_changed_events ALTER COLUMN id SET DEFAULT nextval('geo_repositories_changed_events_id_seq'::regclass);
ALTER TABLE ONLY geo_repository_created_events ALTER COLUMN id SET DEFAULT nextval('geo_repository_created_events_id_seq'::regclass);
ALTER TABLE ONLY geo_repository_deleted_events ALTER COLUMN id SET DEFAULT nextval('geo_repository_deleted_events_id_seq'::regclass);
ALTER TABLE ONLY geo_repository_renamed_events ALTER COLUMN id SET DEFAULT nextval('geo_repository_renamed_events_id_seq'::regclass);
ALTER TABLE ONLY geo_repository_updated_events ALTER COLUMN id SET DEFAULT nextval('geo_repository_updated_events_id_seq'::regclass);
ALTER TABLE ONLY geo_reset_checksum_events ALTER COLUMN id SET DEFAULT nextval('geo_reset_checksum_events_id_seq'::regclass);
ALTER TABLE ONLY ghost_user_migrations ALTER COLUMN id SET DEFAULT nextval('ghost_user_migrations_id_seq'::regclass);
ALTER TABLE ONLY gitlab_subscription_histories ALTER COLUMN id SET DEFAULT nextval('gitlab_subscription_histories_id_seq'::regclass);
@ -21519,12 +21375,6 @@ ALTER TABLE ONLY geo_event_log
ALTER TABLE ONLY geo_events
ADD CONSTRAINT geo_events_pkey PRIMARY KEY (id);
ALTER TABLE ONLY geo_hashed_storage_attachments_events
ADD CONSTRAINT geo_hashed_storage_attachments_events_pkey PRIMARY KEY (id);
ALTER TABLE ONLY geo_hashed_storage_migrated_events
ADD CONSTRAINT geo_hashed_storage_migrated_events_pkey PRIMARY KEY (id);
ALTER TABLE ONLY geo_node_namespace_links
ADD CONSTRAINT geo_node_namespace_links_pkey PRIMARY KEY (id);
@ -21537,21 +21387,6 @@ ALTER TABLE ONLY geo_nodes
ALTER TABLE ONLY geo_repositories_changed_events
ADD CONSTRAINT geo_repositories_changed_events_pkey PRIMARY KEY (id);
ALTER TABLE ONLY geo_repository_created_events
ADD CONSTRAINT geo_repository_created_events_pkey PRIMARY KEY (id);
ALTER TABLE ONLY geo_repository_deleted_events
ADD CONSTRAINT geo_repository_deleted_events_pkey PRIMARY KEY (id);
ALTER TABLE ONLY geo_repository_renamed_events
ADD CONSTRAINT geo_repository_renamed_events_pkey PRIMARY KEY (id);
ALTER TABLE ONLY geo_repository_updated_events
ADD CONSTRAINT geo_repository_updated_events_pkey PRIMARY KEY (id);
ALTER TABLE ONLY geo_reset_checksum_events
ADD CONSTRAINT geo_reset_checksum_events_pkey PRIMARY KEY (id);
ALTER TABLE ONLY ghost_user_migrations
ADD CONSTRAINT ghost_user_migrations_pkey PRIMARY KEY (id);
@ -25681,10 +25516,6 @@ CREATE INDEX index_geo_event_log_on_geo_event_id ON geo_event_log USING btree (g
CREATE INDEX index_geo_event_log_on_repositories_changed_event_id ON geo_event_log USING btree (repositories_changed_event_id) WHERE (repositories_changed_event_id IS NOT NULL);
CREATE INDEX index_geo_hashed_storage_attachments_events_on_project_id ON geo_hashed_storage_attachments_events USING btree (project_id);
CREATE INDEX index_geo_hashed_storage_migrated_events_on_project_id ON geo_hashed_storage_migrated_events USING btree (project_id);
CREATE INDEX index_geo_node_namespace_links_on_geo_node_id ON geo_node_namespace_links USING btree (geo_node_id);
CREATE UNIQUE INDEX index_geo_node_namespace_links_on_geo_node_id_and_namespace_id ON geo_node_namespace_links USING btree (geo_node_id, namespace_id);
@ -25701,18 +25532,6 @@ CREATE INDEX index_geo_nodes_on_primary ON geo_nodes USING btree ("primary");
CREATE INDEX index_geo_repositories_changed_events_on_geo_node_id ON geo_repositories_changed_events USING btree (geo_node_id);
CREATE INDEX index_geo_repository_created_events_on_project_id ON geo_repository_created_events USING btree (project_id);
CREATE INDEX index_geo_repository_deleted_events_on_project_id ON geo_repository_deleted_events USING btree (project_id);
CREATE INDEX index_geo_repository_renamed_events_on_project_id ON geo_repository_renamed_events USING btree (project_id);
CREATE INDEX index_geo_repository_updated_events_on_project_id ON geo_repository_updated_events USING btree (project_id);
CREATE INDEX index_geo_repository_updated_events_on_source ON geo_repository_updated_events USING btree (source);
CREATE INDEX index_geo_reset_checksum_events_on_project_id ON geo_reset_checksum_events USING btree (project_id);
CREATE INDEX index_ghost_user_migrations_on_consume_after_id ON ghost_user_migrations USING btree (consume_after, id);
CREATE UNIQUE INDEX index_ghost_user_migrations_on_user_id ON ghost_user_migrations USING btree (user_id);
@ -26183,6 +26002,8 @@ CREATE INDEX index_members_on_invite_email ON members USING btree (invite_email)
CREATE UNIQUE INDEX index_members_on_invite_token ON members USING btree (invite_token);
CREATE INDEX index_members_on_lower_invite_email ON members USING btree (lower((invite_email)::text));
CREATE INDEX index_members_on_member_namespace_id_compound ON members USING btree (member_namespace_id, type, requested_at, id);
CREATE INDEX index_members_on_member_role_id ON members USING btree (member_role_id);
@ -31439,9 +31260,6 @@ ALTER TABLE ONLY packages_tags
ALTER TABLE ONLY boards_epic_board_positions
ADD CONSTRAINT fk_rails_1ecfd9f2de FOREIGN KEY (epic_id) REFERENCES epics(id) ON DELETE CASCADE;
ALTER TABLE ONLY geo_repository_created_events
ADD CONSTRAINT fk_rails_1f49e46a61 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY external_status_checks
ADD CONSTRAINT fk_rails_1f5a8aa809 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
@ -31538,9 +31356,6 @@ ALTER TABLE ONLY dependency_proxy_image_ttl_group_policies
ALTER TABLE ONLY group_group_links
ADD CONSTRAINT fk_rails_2b2353ca49 FOREIGN KEY (shared_with_group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY geo_repository_updated_events
ADD CONSTRAINT fk_rails_2b70854c08 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY packages_debian_group_component_files
ADD CONSTRAINT fk_rails_2b8992dd83 FOREIGN KEY (architecture_id) REFERENCES packages_debian_group_architectures(id) ON DELETE RESTRICT;
@ -31772,9 +31587,6 @@ ALTER TABLE ONLY snippet_user_mentions
ALTER TABLE ONLY protected_environment_approval_rules
ADD CONSTRAINT fk_rails_4e554f96f5 FOREIGN KEY (protected_environment_id) REFERENCES protected_environments(id) ON DELETE CASCADE;
ALTER TABLE ONLY geo_repository_renamed_events
ADD CONSTRAINT fk_rails_4e6524febb FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY aws_roles
ADD CONSTRAINT fk_rails_4ed56f4720 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
@ -31994,9 +31806,6 @@ ALTER TABLE ONLY vulnerability_findings_remediations
ALTER TABLE ONLY resource_iteration_events
ADD CONSTRAINT fk_rails_6830c13ac1 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
ALTER TABLE ONLY geo_hashed_storage_migrated_events
ADD CONSTRAINT fk_rails_687ed7d7c5 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY plan_limits
ADD CONSTRAINT fk_rails_69f8b6184f FOREIGN KEY (plan_id) REFERENCES plans(id) ON DELETE CASCADE;
@ -32282,9 +32091,6 @@ ALTER TABLE ONLY approval_project_rules_groups
ALTER TABLE ONLY vulnerability_occurrences
ADD CONSTRAINT fk_rails_90fed4faba FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY geo_reset_checksum_events
ADD CONSTRAINT fk_rails_910a06f12b FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY project_error_tracking_settings
ADD CONSTRAINT fk_rails_910a2b8bd9 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
@ -32771,9 +32577,6 @@ ALTER TABLE ONLY alert_management_alert_assignees
ALTER TABLE ONLY packages_terraform_module_metadata
ADD CONSTRAINT fk_rails_d48f21a84b FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE SET NULL;
ALTER TABLE ONLY geo_hashed_storage_attachments_events
ADD CONSTRAINT fk_rails_d496b088e9 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE p_ci_job_annotations
ADD CONSTRAINT fk_rails_d4d0c0fa0f FOREIGN KEY (partition_id, job_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;

View File

@ -7599,6 +7599,24 @@ Input type: `RepositionImageDiffNoteInput`
| <a id="mutationrepositionimagediffnoteerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationrepositionimagediffnotenote"></a>`note` | [`Note`](#note) | Note after mutation. |
### `Mutation.runnerCacheClear`
Input type: `RunnerCacheClearInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationrunnercacheclearclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationrunnercacheclearprojectid"></a>`projectId` | [`ProjectID!`](#projectid) | Global ID of the project that will have its runner cache cleared. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationrunnercacheclearclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationrunnercacheclearerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
### `Mutation.runnerCreate`
DETAILS:
@ -22263,6 +22281,8 @@ Returns [`WorkItemStateCountsType`](#workitemstatecountstype).
| <a id="groupworkitemstatecountsiid"></a>`iid` | [`String`](#string) | IID of the work item. For example, "1". |
| <a id="groupworkitemstatecountsiids"></a>`iids` | [`[String!]`](#string) | List of IIDs of work items. For example, `["1", "2"]`. |
| <a id="groupworkitemstatecountsin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.'. |
| <a id="groupworkitemstatecountsincludeancestors"></a>`includeAncestors` | [`Boolean`](#boolean) | Include work items from ancestor groups. |
| <a id="groupworkitemstatecountsincludedescendants"></a>`includeDescendants` | [`Boolean`](#boolean) | Include work items from descendant groups and projects. |
| <a id="groupworkitemstatecountsrequirementlegacywidget"></a>`requirementLegacyWidget` **{warning-solid}** | [`RequirementLegacyFilterInput`](#requirementlegacyfilterinput) | **Deprecated** in GitLab 15.9. Use work item IID filter instead. |
| <a id="groupworkitemstatecountssearch"></a>`search` | [`String`](#string) | Search query for title or description. |
| <a id="groupworkitemstatecountssort"></a>`sort` | [`WorkItemSort`](#workitemsort) | Sort work items by criteria. |
@ -22308,6 +22328,8 @@ four standard [pagination arguments](#pagination-arguments):
| <a id="groupworkitemsiid"></a>`iid` | [`String`](#string) | IID of the work item. For example, "1". |
| <a id="groupworkitemsiids"></a>`iids` | [`[String!]`](#string) | List of IIDs of work items. For example, `["1", "2"]`. |
| <a id="groupworkitemsin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.'. |
| <a id="groupworkitemsincludeancestors"></a>`includeAncestors` | [`Boolean`](#boolean) | Include work items from ancestor groups. |
| <a id="groupworkitemsincludedescendants"></a>`includeDescendants` | [`Boolean`](#boolean) | Include work items from descendant groups and projects. |
| <a id="groupworkitemsrequirementlegacywidget"></a>`requirementLegacyWidget` **{warning-solid}** | [`RequirementLegacyFilterInput`](#requirementlegacyfilterinput) | **Deprecated** in GitLab 15.9. Use work item IID filter instead. |
| <a id="groupworkitemssearch"></a>`search` | [`String`](#string) | Search query for title or description. |
| <a id="groupworkitemssort"></a>`sort` | [`WorkItemSort`](#workitemsort) | Sort work items by criteria. |

View File

@ -110,6 +110,26 @@ We've deprecated the use of `ref` and `sha` in API calls to `GET /projects/:id/c
<div class="deprecation breaking-change" data-milestone="18.0">
### Breaking change to the Maven repository group permissions
<div class="deprecation-notes">
- Announced in GitLab <span class="milestone">16.6</span>
- Removal in GitLab <span class="milestone">18.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/393933).
</div>
The Maven repository exposes an API endpoint at the group level that allows Maven clients to download files from a specific package. The package finder first locates the package within the group, and then finds the file within the package.
However, there is a limitation that affects duplicate package names hosted in different projects. The Maven package finder always returns the most recent package, but the "most recent" filter depends on user permissions. It is possible for a user with different permissions in different projects to download the wrong Maven package.
In GitLab 18.0, the package finder logic will be fixed so that the "most recent" package is the last updated name and version of a package in a group. User permissions will be checked after the most recent package is found.
After the change, download requests for users without correct permissions will be rejected. If your workflow depends on the current bugged behavior, this fix will introduce a breaking change.
The change will be introduced in GitLab 16.6 behind a feature flag. If you are interested in enabling the feature flag for your group, leave a comment in [issue 393933](https://gitlab.com/gitlab-org/gitlab/-/issues/393933).
</div>
<div class="deprecation breaking-change" data-milestone="18.0">
### Default CI/CD job token (`CI_JOB_TOKEN`) scope changed
<div class="deprecation-notes">
@ -134,6 +154,24 @@ In 16.3, the names of these settings were changed to clarify their meanings: the
<div class="deprecation breaking-change" data-milestone="18.0">
### Dependency Proxy: Access tokens to have additional scope checks
<div class="deprecation-notes">
- Announced in GitLab <span class="milestone">16.7</span>
- Removal in GitLab <span class="milestone">18.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/431386).
</div>
When using the Dependency Proxy for containers with a group access token or personal access token, `docker login` and `docker pull` requests with insufficient scopes for Dependency Proxy are not rejected.
GitLab 18.0 adds checks for group or personal access tokens authenticating with the dependency proxy for containers. This is a breaking change, because tokens without the required scopes will fail.
To help avoid being impacted by this breaking change, create new access tokens with the [required scopes](https://docs.gitlab.com/ee/user/packages/dependency_proxy/#authenticate-with-the-dependency-proxy), and update your workflow variables and scripts with those new tokens.
</div>
<div class="deprecation breaking-change" data-milestone="18.0">
### Deprecate License Scanning CI/CD artifact report type
<div class="deprecation-notes">
@ -708,26 +746,6 @@ can change `## Step - 1` to `## Step 1` to ensure in-page links continue to work
<div class="deprecation breaking-change" data-milestone="17.0">
### Breaking change to the Maven repository group permissions
<div class="deprecation-notes">
- Announced in GitLab <span class="milestone">16.6</span>
- Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/393933).
</div>
The Maven repository exposes an API endpoint at the group level that allows Maven clients to download files from a specific package. The package finder first locates the package within the group, and then finds the file within the package.
However, there is a limitation that affects duplicate package names hosted in different projects. The Maven package finder always returns the most recent package, but the "most recent" filter depends on user permissions. It is possible for a user with different permissions in different projects to download the wrong Maven package.
In GitLab 17.0, the package finder logic will be fixed so that the "most recent" package is the last updated name and version of a package in a group. User permissions will be checked after the most recent package is found.
After the change, download requests for users without correct permissions will be rejected. If your workflow depends on the current bugged behavior, this fix will introduce a breaking change.
The change will be introduced in GitLab 16.6 behind a feature flag. If you are interested in enabling the feature flag for your group, leave a comment in [issue 393933](https://gitlab.com/gitlab-org/gitlab/-/issues/393933).
</div>
<div class="deprecation breaking-change" data-milestone="17.0">
### CiRunner.projects default sort is changing to `id_desc`
<div class="deprecation-notes">
@ -794,24 +812,6 @@ These three variables will be removed in GitLab 17.0.
<div class="deprecation breaking-change" data-milestone="17.0">
### Dependency Proxy: Access tokens to have additional scope checks
<div class="deprecation-notes">
- Announced in GitLab <span class="milestone">16.7</span>
- Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/431386).
</div>
When using the Dependency Proxy for containers with a group access token or personal access token, `docker login` and `docker pull` requests with insufficient scopes for Dependency Proxy are not rejected.
GitLab 17.0 adds checks for group or personal access tokens authenticating with the dependency proxy for containers. This is a breaking change, because tokens without the required scopes will fail.
To help avoid being impacted by this breaking change, create new access tokens with the [required scopes](https://docs.gitlab.com/ee/user/packages/dependency_proxy/#authenticate-with-the-dependency-proxy), and update your workflow variables and scripts with those new tokens.
</div>
<div class="deprecation breaking-change" data-milestone="17.0">
### Dependency Scanning incorrect SBOM metadata properties
<div class="deprecation-notes">

176
doc/user/ai_experiments.md Normal file
View File

@ -0,0 +1,176 @@
---
stage: AI-powered
group: AI Model Validation
description: AI-powered features and functionality.
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# GitLab Duo Experiments
The following GitLab Duo features are in the
[Experiment](../policy/experiment-beta-support.md#experiment) phase.
## Explain code in the Web UI with Code explanation
DETAILS:
**Tier:** Freely available for Premium and Ultimate for a limited time. In the future, will require Premium or Ultimate with [GitLab Duo Pro](../subscriptions/subscription-add-ons.md).
**Offering:** GitLab.com
**Status:** Experiment
> - Introduced in GitLab 15.11 as an [Experiment](../policy/experiment-beta-support.md#experiment) on GitLab.com.
To use this feature:
- The parent group of the project must:
- Enable the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features).
- You must:
- Belong to at least one group with the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features) enabled.
- Have sufficient permissions to view the project.
GitLab can help you get up to speed faster if you:
- Spend a lot of time trying to understand pieces of code that others have created, or
- Struggle to understand code written in a language that you are not familiar with.
By using a large language model, GitLab can explain the code in natural language.
To explain your code:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select any file in your project that contains code.
1. On the file, select the lines that you want to have explained.
1. On the left side, select the question mark (**{question}**). You might have to scroll to the first line of your selection to view it. This sends the selected code, together with a prompt, to provide an explanation to the large language model.
1. A drawer is displayed on the right side of the page. Wait a moment for the explanation to be generated.
1. Provide feedback about how satisfied you are with the explanation, so we can improve the results.
You can also have code explained in the context of a merge request. To explain
code in a merge request:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Code > Merge requests**, then select your merge request.
1. On the secondary menu, select **Changes**.
1. On the file you would like explained, select the three dots (**{ellipsis_v}**) and select **View File @ $SHA**.
A separate browser tab opens and shows the full file with the latest changes.
1. On the new tab, select the lines that you want to have explained.
1. On the left side, select the question mark (**{question}**). You might have to scroll to the first line of your selection to view it. This sends the selected code, together with a prompt, to provide an explanation to the large language model.
1. A drawer is displayed on the right side of the page. Wait a moment for the explanation to be generated.
1. Provide feedback about how satisfied you are with the explanation, so we can improve the results.
![How to use the Explain Code Experiment](img/explain_code_experiment.png)
We cannot guarantee that the large language model produces results that are correct. Use the explanation with caution.
## Summarize issue discussions with Discussion summary
DETAILS:
**Tier:** Freely available for Ultimate for a limited time. In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md).
**Offering:** GitLab.com
**Status:** Experiment
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10344) in GitLab 16.0 as an [Experiment](../policy/experiment-beta-support.md#experiment).
To use this feature:
- The parent group of the issue must:
- Enable the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features).
- You must:
- Belong to at least one group with the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features) enabled.
- Have sufficient permissions to view the issue.
You can generate a summary of discussions on an issue:
1. In an issue, scroll to the **Activity** section.
1. Select **View summary**.
The comments in the issue are summarized in as many as 10 list items.
The summary is displayed only for you.
Provide feedback on this experimental feature in [issue 407779](https://gitlab.com/gitlab-org/gitlab/-/issues/407779).
**Data usage**: When you use this feature, the text of all comments on the issue are sent to the large
language model referenced above.
## Forecast deployment frequency with Value stream forecasting
DETAILS:
**Tier:** Freely available for Ultimate for a limited time. In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md).
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
**Status:** Experiment
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10228) in GitLab 16.2 as an [Experiment](../policy/experiment-beta-support.md#experiment).
To use this feature:
- The parent group of the project must:
- Enable the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features).
- You must:
- Belong to at least one group with the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features) enabled.
- Have sufficient permissions to view the CI/CD analytics.
In CI/CD Analytics, you can view a forecast of deployment frequency:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Analyze > CI/CD analytics**.
1. Select the **Deployment frequency** tab.
1. Turn on the **Show forecast** toggle.
1. On the confirmation dialog, select **Accept testing terms**.
The forecast is displayed as a dotted line on the chart. Data is forecasted for a duration that is half of the selected date range.
For example, if you select a 30-day range, a forecast for the following 15 days is displayed.
![Forecast deployment frequency](img/forecast_deployment_frequency.png)
Provide feedback on this experimental feature in [issue 416833](https://gitlab.com/gitlab-org/gitlab/-/issues/416833).
## Root cause analysis
DETAILS:
**Tier:** Freely available for Ultimate for a limited time. In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md).
**Offering:** GitLab.com
**Status:** Experiment
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123692) in GitLab 16.2 as an [Experiment](../policy/experiment-beta-support.md#experiment).
To use this feature:
- The parent group of the project must:
- Enable the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features).
- You must:
- Belong to at least one group with the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features) enabled.
- Have sufficient permissions to view the CI/CD job.
When the feature is available, the "Root cause analysis" button will appears on
a failed CI/CD job. Selecting this button generates an analysis regarding the
reason for the failure.
## Summarize an issue with Issue description generation
DETAILS:
**Tier:** Freely available for Ultimate for a limited time. In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md).
**Offering:** GitLab.com
**Status:** Experiment
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10762) in GitLab 16.3 as an [Experiment](../policy/experiment-beta-support.md#experiment).
To use this feature:
- The parent group of the project must:
- Enable the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features).
- You must:
- Belong to at least one group with the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features) enabled.
- Have sufficient permissions to view the issue.
You can generate the description for an issue from a short summary.
1. Create a new issue.
1. Above the **Description** field, select **AI actions > Generate issue description**.
1. Write a short description and select **Submit**.
The issue description is replaced with AI-generated text.
Provide feedback on this experimental feature in [issue 409844](https://gitlab.com/gitlab-org/gitlab/-/issues/409844).
**Data usage**: When you use this feature, the text you enter is sent to the large
language model referenced above.

View File

@ -13,7 +13,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
GitLab Duo is a set of AI-assisted features across the GitLab DevSecOps platform. These features aim to help increase velocity and solve key pain points across the software development lifecycle. GitLab Duo features are accessible through the [IDE extension](../editor_extensions/index.md) and the GitLab UI. Some of the features are also accessible through [GitLab Duo Chat](gitlab_duo_chat.md), which is available in both interfaces.
Some features are still in development. View details about [support for each status](../policy/experiment-beta-support.md#experiment) (Experiment, Beta, Generally Available). Learn more about how to [enable GitLab Duo features](ai_features_enable.md).
Some features are still in development. [View features in the Experiment phase](ai_experiments.md).
Learn more about how to [turn GitLab Duo features on and off](ai_features_enable.md).
GitLab is [transparent](https://handbook.gitlab.com/handbook/values/#transparency). As GitLab Duo features mature, the documentation will be updated to clearly state how and where you can access these capabilities.
@ -22,8 +24,8 @@ GitLab is [transparent](https://handbook.gitlab.com/handbook/values/#transparenc
| Helps you write code more efficiently by showing code suggestions as you type.<br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=hCAyCTacdAQ) | [Code Suggestions](project/repository/code_suggestions/index.md) | **Tier:** Premium and Ultimate with [GitLab Duo Pro](../subscriptions/subscription-add-ons.md)<br>**Offering:** GitLab.com, Self-managed, GitLab Dedicated <br>**Status:** Generally Available |
| Processes and generates text and code in a conversational manner. Helps you quickly identify useful information in large volumes of text in issues, epics, code, and GitLab documentation. | [Chat](gitlab_duo_chat.md) | **Tier:** Freely available for Premium and Ultimate for a limited time<br>**Offering:** GitLab.com, Self-managed, GitLab Dedicated <br>**Status:** Generally Available |
| Helps you discover or recall Git commands when and where you need them. | [Git suggestions](../editor_extensions/gitlab_cli/index.md#gitlab-duo-commands) | **Tier:** Freely available for Ultimate for a limited time<br>In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md)<br>**Offering:** GitLab.com<br>**Status:** Experiment |
| Assists with quickly getting everyone up to speed on lengthy conversations to help ensure you are all on the same page. <br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=IcdxLfTIUgc) | [Discussion summary](#summarize-issue-discussions-with-discussion-summary) | **Tier:** Freely available for Ultimate for a limited time<br>In the future, will require [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md)<br>**Offering:** GitLab.com <br>**Status:** Experiment |
| Generates issue descriptions. | [Issue description generation](#summarize-an-issue-with-issue-description-generation) | **Tier:** Freely available for Ultimate for a limited time<br>In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md)<br>**Offering:** GitLab.com <br>**Status:** Experiment |
| Assists with quickly getting everyone up to speed on lengthy conversations to help ensure you are all on the same page. <br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=IcdxLfTIUgc) | [Discussion summary](ai_experiments.md#summarize-issue-discussions-with-discussion-summary) | **Tier:** Freely available for Ultimate for a limited time<br>In the future, will require [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md)<br>**Offering:** GitLab.com <br>**Status:** Experiment |
| Generates issue descriptions. | [Issue description generation](ai_experiments.md#summarize-an-issue-with-issue-description-generation) | **Tier:** Freely available for Ultimate for a limited time<br>In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md)<br>**Offering:** GitLab.com <br>**Status:** Experiment |
| Automates repetitive tasks and helps catch bugs early. <br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=g6MS1JsRWgs) | [Test generation](gitlab_duo_chat.md#write-tests-in-the-ide) | **Tier:** Freely available for Premium and Ultimate for a limited time<br>In the future, will require Premium or Ultimate with [GitLab Duo Pro](../subscriptions/subscription-add-ons.md)<br>**Offering:** GitLab.com, Self-managed, GitLab Dedicated <br>**Status:** Beta |
| Generates a description for the merge request based on the contents of the template. | [Merge request template population](project/merge_requests/ai_in_merge_requests.md#fill-in-merge-request-templates) | **Tier:** Freely available for Ultimate for a limited time<br>In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md)<br>**Offering:** GitLab.com<br>**Status:** Experiment |
| Assists in creating faster and higher-quality reviews by automatically suggesting reviewers for your merge request. <br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=ivwZQgh4Rxw) | [Suggested Reviewers](project/merge_requests/reviews/index.md#gitlab-duo-suggested-reviewers) | **Tier:** Ultimate <br>**Offering:** GitLab.com<br>**Status:** Generally Available |
@ -31,192 +33,22 @@ GitLab is [transparent](https://handbook.gitlab.com/handbook/values/#transparenc
| Helps ease merge request handoff between authors and reviewers and help reviewers efficiently understand suggestions. | [Code review summary](project/merge_requests/ai_in_merge_requests.md#summarize-my-merge-request-review) | **Tier:** Freely available for Ultimate for a limited time<br>In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md)<br>**Offering:** GitLab.com <br>**Status:** Experiment |
| Helps you remediate vulnerabilities more efficiently, boost your skills, and write more secure code. <br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=6sDf73QOav8) | [Vulnerability explanation](application_security/vulnerabilities/index.md#explaining-a-vulnerability) | **Tier:** Freely available for Ultimate for a limited time<br>In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md) <br>**Offering:** GitLab.com <br>**Status:** Beta |
| Generates a merge request containing the changes required to mitigate a vulnerability. | [Vulnerability resolution](application_security/vulnerabilities/index.md#vulnerability-resolution) | **Tier:** Freely available for Ultimate for a limited time<br>In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md)<br>**Offering:** GitLab.com <br>**Status:** Experiment |
| Helps you understand code by explaining it in English language. <br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=1izKaLmmaCA) | [Code explanation](#explain-code-in-the-web-ui-with-code-explanation) | **Tier:** Freely available for Premium and Ultimate for a limited time<br>In the future, will require Premium or Ultimate with [GitLab Duo Pro](../subscriptions/subscription-add-ons.md) <br>**Offering:** GitLab.com <br>**Status:** Experiment |
| Assists you in determining the root cause for a pipeline failure and failed CI/CD build. | [Root cause analysis](#root-cause-analysis) | **Tier:** Freely available for Ultimate for a limited time<br>In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md)<br>**Offering:** GitLab.com <br>**Status:** Experiment |
| Assists you with predicting productivity metrics and identifying anomalies across your software development lifecycle. | [Value stream forecasting](#forecast-deployment-frequency-with-value-stream-forecasting) | **Tier:** Freely available for Ultimate for a limited time<br>In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md) <br>**Offering:** GitLab.com, Self-managed, GitLab Dedicated <br>**Status:** Experiment |
| Helps you understand code by explaining it in English language. <br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=1izKaLmmaCA) | [Code explanation](ai_experiments.md#explain-code-in-the-web-ui-with-code-explanation) | **Tier:** Freely available for Premium and Ultimate for a limited time<br>In the future, will require Premium or Ultimate with [GitLab Duo Pro](../subscriptions/subscription-add-ons.md) <br>**Offering:** GitLab.com <br>**Status:** Experiment |
| Assists you in determining the root cause for a pipeline failure and failed CI/CD build. | [Root cause analysis](ai_experiments.md#root-cause-analysis) | **Tier:** Freely available for Ultimate for a limited time<br>In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md)<br>**Offering:** GitLab.com <br>**Status:** Experiment |
| Assists you with predicting productivity metrics and identifying anomalies across your software development lifecycle. | [Value stream forecasting](ai_experiments.md#forecast-deployment-frequency-with-value-stream-forecasting) | **Tier:** Freely available for Ultimate for a limited time<br>In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md) <br>**Offering:** GitLab.com, Self-managed, GitLab Dedicated <br>**Status:** Experiment |
| Processes and responds to your questions about your application's usage data. | [Product Analytics](analytics/analytics_dashboards.md#generate-a-custom-visualization-with-gitlab-duo) | **Tier:** Freely available for Ultimate for a limited time<br>In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md) <br>**Offering:** GitLab.com <br>**Status:** Experiment |
## Disable GitLab Duo features for specific groups or projects or an entire instance
Disable GitLab Duo features by [following these instructions](ai_features_enable.md).
## Experimental AI features and how to use them
The following subsections describe GitLab Duo features that are in the
Experiment phase.
### Explain code in the Web UI with Code explanation
DETAILS:
**Tier:** Freely available for Premium and Ultimate for a limited time. In the future, will require Premium or Ultimate with [GitLab Duo Pro](../subscriptions/subscription-add-ons.md).
**Offering:** GitLab.com
**Status:** Experiment
> - Introduced in GitLab 15.11 as an [Experiment](../policy/experiment-beta-support.md#experiment) on GitLab.com.
To use this feature:
- The parent group of the project must:
- Enable the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features).
- You must:
- Belong to at least one group with the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features) enabled.
- Have sufficient permissions to view the project.
GitLab can help you get up to speed faster if you:
- Spend a lot of time trying to understand pieces of code that others have created, or
- Struggle to understand code written in a language that you are not familiar with.
By using a large language model, GitLab can explain the code in natural language.
To explain your code:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select any file in your project that contains code.
1. On the file, select the lines that you want to have explained.
1. On the left side, select the question mark (**{question}**). You might have to scroll to the first line of your selection to view it. This sends the selected code, together with a prompt, to provide an explanation to the large language model.
1. A drawer is displayed on the right side of the page. Wait a moment for the explanation to be generated.
1. Provide feedback about how satisfied you are with the explanation, so we can improve the results.
You can also have code explained in the context of a merge request. To explain
code in a merge request:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Code > Merge requests**, then select your merge request.
1. On the secondary menu, select **Changes**.
1. On the file you would like explained, select the three dots (**{ellipsis_v}**) and select **View File @ $SHA**.
A separate browser tab opens and shows the full file with the latest changes.
1. On the new tab, select the lines that you want to have explained.
1. On the left side, select the question mark (**{question}**). You might have to scroll to the first line of your selection to view it. This sends the selected code, together with a prompt, to provide an explanation to the large language model.
1. A drawer is displayed on the right side of the page. Wait a moment for the explanation to be generated.
1. Provide feedback about how satisfied you are with the explanation, so we can improve the results.
![How to use the Explain Code Experiment](img/explain_code_experiment.png)
We cannot guarantee that the large language model produces results that are correct. Use the explanation with caution.
### Summarize issue discussions with Discussion summary
DETAILS:
**Tier:** Freely available for Ultimate for a limited time. In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md).
**Offering:** GitLab.com
**Status:** Experiment
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10344) in GitLab 16.0 as an [Experiment](../policy/experiment-beta-support.md#experiment).
To use this feature:
- The parent group of the issue must:
- Enable the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features).
- You must:
- Belong to at least one group with the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features) enabled.
- Have sufficient permissions to view the issue.
You can generate a summary of discussions on an issue:
1. In an issue, scroll to the **Activity** section.
1. Select **View summary**.
The comments in the issue are summarized in as many as 10 list items.
The summary is displayed only for you.
Provide feedback on this experimental feature in [issue 407779](https://gitlab.com/gitlab-org/gitlab/-/issues/407779).
**Data usage**: When you use this feature, the text of all comments on the issue are sent to the large
language model referenced above.
### Forecast deployment frequency with Value stream forecasting
DETAILS:
**Tier:** Freely available for Ultimate for a limited time. In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md).
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
**Status:** Experiment
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10228) in GitLab 16.2 as an [Experiment](../policy/experiment-beta-support.md#experiment).
To use this feature:
- The parent group of the project must:
- Enable the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features).
- You must:
- Belong to at least one group with the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features) enabled.
- Have sufficient permissions to view the CI/CD analytics.
In CI/CD Analytics, you can view a forecast of deployment frequency:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Analyze > CI/CD analytics**.
1. Select the **Deployment frequency** tab.
1. Turn on the **Show forecast** toggle.
1. On the confirmation dialog, select **Accept testing terms**.
The forecast is displayed as a dotted line on the chart. Data is forecasted for a duration that is half of the selected date range.
For example, if you select a 30-day range, a forecast for the following 15 days is displayed.
![Forecast deployment frequency](img/forecast_deployment_frequency.png)
Provide feedback on this experimental feature in [issue 416833](https://gitlab.com/gitlab-org/gitlab/-/issues/416833).
### Root cause analysis
DETAILS:
**Tier:** Freely available for Ultimate for a limited time. In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md).
**Offering:** GitLab.com
**Status:** Experiment
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123692) in GitLab 16.2 as an [Experiment](../policy/experiment-beta-support.md#experiment).
To use this feature:
- The parent group of the project must:
- Enable the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features).
- You must:
- Belong to at least one group with the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features) enabled.
- Have sufficient permissions to view the CI/CD job.
When the feature is available, the "Root cause analysis" button will appears on
a failed CI/CD job. Selecting this button generates an analysis regarding the
reason for the failure.
### Summarize an issue with Issue description generation
DETAILS:
**Tier:** Freely available for Ultimate for a limited time. In the future, will require Ultimate with [GitLab Duo Enterprise](../subscriptions/subscription-add-ons.md).
**Offering:** GitLab.com
**Status:** Experiment
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10762) in GitLab 16.3 as an [Experiment](../policy/experiment-beta-support.md#experiment).
To use this feature:
- The parent group of the project must:
- Enable the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features).
- You must:
- Belong to at least one group with the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features) enabled.
- Have sufficient permissions to view the issue.
You can generate the description for an issue from a short summary.
1. Create a new issue.
1. Above the **Description** field, select **AI actions > Generate issue description**.
1. Write a short description and select **Submit**.
The issue description is replaced with AI-generated text.
Provide feedback on this experimental feature in [issue 409844](https://gitlab.com/gitlab-org/gitlab/-/issues/409844).
**Data usage**: When you use this feature, the text you enter is sent to the large
language model referenced above.
## Language models
| Feature | Large Language Model |
|---|---|
| [Git suggestions](https://gitlab.com/gitlab-org/gitlab/-/issues/409636) | Vertex AI Codey [`codechat-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/code-chat) |
| [Discussion summary](#summarize-issue-discussions-with-discussion-summary) |Anthropic [`Claude-2.1`](https://docs.anthropic.com/claude/docs/models-overview#model-comparison) |
| [Issue description generation](#summarize-an-issue-with-issue-description-generation) | Anthropic [`Claude-2.1`](https://docs.anthropic.com/claude/docs/models-overview#model-comparison) |
| [Discussion summary](ai_experiments.md#summarize-issue-discussions-with-discussion-summary) |Anthropic [`Claude-2.1`](https://docs.anthropic.com/claude/docs/models-overview#model-comparison) |
| [Issue description generation](ai_experiments.md#summarize-an-issue-with-issue-description-generation) | Anthropic [`Claude-2.1`](https://docs.anthropic.com/claude/docs/models-overview#model-comparison) |
| [Code Suggestions](project/repository/code_suggestions/index.md) | For Code Completion: Vertex AI Codey [`code-gecko`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/code-completion) For Code Generation: Anthropic [`Claude-3-Sonnet`](https://docs.anthropic.com/claude/docs/models-overview) |
| [Test generation](gitlab_duo_chat.md#write-tests-in-the-ide) | Anthropic [`Claude-2.1`](https://docs.anthropic.com/claude/docs/models-overview#model-comparison) |
| [Merge request template population](project/merge_requests/ai_in_merge_requests.md#fill-in-merge-request-templates) | Vertex AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text) |
@ -225,10 +57,10 @@ language model referenced above.
| [Code review summary](project/merge_requests/ai_in_merge_requests.md#summarize-my-merge-request-review) | Vertex AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text) |
| [Vulnerability explanation](application_security/vulnerabilities/index.md#explaining-a-vulnerability) | Vertex AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text) Anthropic [`Claude-2.1`](https://docs.anthropic.com/claude/docs/models-overview#model-comparison) if degraded performance |
| [Vulnerability resolution](application_security/vulnerabilities/index.md#explaining-a-vulnerability) | Vertex AI Codey [`code-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/code-generation) |
| [Code explanation](#explain-code-in-the-web-ui-with-code-explanation) | Vertex AI Codey [`codechat-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/code-chat) |
| [Code explanation](ai_experiments.md#explain-code-in-the-web-ui-with-code-explanation) | Vertex AI Codey [`codechat-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/code-chat) |
| [GitLab Duo Chat](gitlab_duo_chat.md) | Anthropic [`Claude-2.1`](https://docs.anthropic.com/claude/docs/models-overview#model-comparison) Vertex AI Codey [`textembedding-gecko`](https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-text-embeddings) |
| [Root cause analysis](#root-cause-analysis) | Vertex AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text) |
| [Value stream forecasting](#forecast-deployment-frequency-with-value-stream-forecasting) | Statistical forecasting |
| [Root cause analysis](ai_experiments.md#root-cause-analysis) | Vertex AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text) |
| [Value stream forecasting](ai_experiments.md#forecast-deployment-frequency-with-value-stream-forecasting) | Statistical forecasting |
| [Product analytics](analytics/analytics_dashboards.md#generate-a-custom-visualization-with-gitlab-duo) | Vertex AI Codey [`codechat-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/code-chat) |
## Data usage

View File

@ -110,7 +110,7 @@ You can also ask to explain specific job errors by copy-pasting the error messag
- `Please explain this CI/CD job error message in the context of a Go project: build.sh: line 14: go command not found`
Alternatively, you can use [root cause analysis in CI/CD](ai_features.md#root-cause-analysis).
Alternatively, you can use [root cause analysis in CI/CD](ai_experiments.md#root-cause-analysis).
For more practical examples, see the [GitLab Duo examples](gitlab_duo_examples.md).

Some files were not shown because too many files have changed in this diff Show More