Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-10-08 21:20:07 +00:00
parent aab8c3eabf
commit 61829a6fc9
72 changed files with 668 additions and 421 deletions

View File

@ -593,7 +593,7 @@ group :test do
# Moved in `test` because https://gitlab.com/gitlab-org/gitlab/-/issues/217527
gem 'derailed_benchmarks', require: false # rubocop:todo Gemfile/MissingFeatureCategory
gem 'gitlab_quality-test_tooling', '~> 1.38.0', require: false, feature_category: :tooling
gem 'gitlab_quality-test_tooling', '~> 1.39.0', require: false, feature_category: :tooling
end
gem 'octokit', '~> 9.0', feature_category: :importers

View File

@ -240,7 +240,7 @@
{"name":"gitlab-styles","version":"12.0.1","platform":"ruby","checksum":"d8a302b0ab0e1f18e2d11501760f1b85c5e70b5e5ca628828a0786c7984ed133"},
{"name":"gitlab_chronic_duration","version":"0.12.0","platform":"ruby","checksum":"0d766944d415b5c831f176871ee8625783fc0c5bfbef2d79a3a616f207ffc16d"},
{"name":"gitlab_omniauth-ldap","version":"2.2.0","platform":"ruby","checksum":"bb4d20acb3b123ed654a8f6a47d3fac673ece7ed0b6992edb92dca14bad2838c"},
{"name":"gitlab_quality-test_tooling","version":"1.38.1","platform":"ruby","checksum":"7c5e71b9069ef69ef9e0d07fb26af6d6afcc82a5e898e62a8049df05ebd75c10"},
{"name":"gitlab_quality-test_tooling","version":"1.39.0","platform":"ruby","checksum":"3b3cb8495c69b5b1358cb114c0d41f371badbc7fd9a137ff7a4e1442a8a6ea35"},
{"name":"globalid","version":"1.1.0","platform":"ruby","checksum":"b337e1746f0c8cb0a6c918234b03a1ddeb4966206ce288fbb57779f59b2d154f"},
{"name":"gon","version":"6.4.0","platform":"ruby","checksum":"e3a618d659392890f1aa7db420f17c75fd7d35aeb5f8fe003697d02c4b88d2f0"},
{"name":"google-apis-androidpublisher_v3","version":"0.34.0","platform":"ruby","checksum":"d7e1d7dd92f79c498fe2082222a1740d788e022e660c135564b3fd299cab5425"},

View File

@ -779,7 +779,7 @@ GEM
omniauth (>= 1.3, < 3)
pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
rubyntlm (~> 0.5)
gitlab_quality-test_tooling (1.38.1)
gitlab_quality-test_tooling (1.39.0)
activesupport (>= 7.0, < 7.2)
amatch (~> 0.4.1)
fog-google (~> 1.24, >= 1.24.1)
@ -2090,7 +2090,7 @@ DEPENDENCIES
gitlab-utils!
gitlab_chronic_duration (~> 0.12)
gitlab_omniauth-ldap (~> 2.2.0)
gitlab_quality-test_tooling (~> 1.38.0)
gitlab_quality-test_tooling (~> 1.39.0)
gon (~> 6.4.0)
google-apis-androidpublisher_v3 (~> 0.34.0)
google-apis-cloudbilling_v1 (~> 0.21.0)

View File

@ -241,7 +241,7 @@
{"name":"gitlab-styles","version":"12.0.1","platform":"ruby","checksum":"d8a302b0ab0e1f18e2d11501760f1b85c5e70b5e5ca628828a0786c7984ed133"},
{"name":"gitlab_chronic_duration","version":"0.12.0","platform":"ruby","checksum":"0d766944d415b5c831f176871ee8625783fc0c5bfbef2d79a3a616f207ffc16d"},
{"name":"gitlab_omniauth-ldap","version":"2.2.0","platform":"ruby","checksum":"bb4d20acb3b123ed654a8f6a47d3fac673ece7ed0b6992edb92dca14bad2838c"},
{"name":"gitlab_quality-test_tooling","version":"1.38.1","platform":"ruby","checksum":"7c5e71b9069ef69ef9e0d07fb26af6d6afcc82a5e898e62a8049df05ebd75c10"},
{"name":"gitlab_quality-test_tooling","version":"1.39.0","platform":"ruby","checksum":"3b3cb8495c69b5b1358cb114c0d41f371badbc7fd9a137ff7a4e1442a8a6ea35"},
{"name":"globalid","version":"1.1.0","platform":"ruby","checksum":"b337e1746f0c8cb0a6c918234b03a1ddeb4966206ce288fbb57779f59b2d154f"},
{"name":"gon","version":"6.4.0","platform":"ruby","checksum":"e3a618d659392890f1aa7db420f17c75fd7d35aeb5f8fe003697d02c4b88d2f0"},
{"name":"google-apis-androidpublisher_v3","version":"0.34.0","platform":"ruby","checksum":"d7e1d7dd92f79c498fe2082222a1740d788e022e660c135564b3fd299cab5425"},

View File

@ -789,7 +789,7 @@ GEM
omniauth (>= 1.3, < 3)
pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
rubyntlm (~> 0.5)
gitlab_quality-test_tooling (1.38.1)
gitlab_quality-test_tooling (1.39.0)
activesupport (>= 7.0, < 7.2)
amatch (~> 0.4.1)
fog-google (~> 1.24, >= 1.24.1)
@ -2117,7 +2117,7 @@ DEPENDENCIES
gitlab-utils!
gitlab_chronic_duration (~> 0.12)
gitlab_omniauth-ldap (~> 2.2.0)
gitlab_quality-test_tooling (~> 1.38.0)
gitlab_quality-test_tooling (~> 1.39.0)
gon (~> 6.4.0)
google-apis-androidpublisher_v3 (~> 0.34.0)
google-apis-cloudbilling_v1 (~> 0.21.0)

View File

@ -1,55 +0,0 @@
<script>
import { GlDisclosureDropdown } from '@gitlab/ui';
import { InternalEvents } from '~/tracking';
import { __ } from '~/locale';
const trackingMixin = InternalEvents.mixin();
export default {
components: {
GlDisclosureDropdown,
},
mixins: [trackingMixin],
inject: {
switchDashboardPath: { default: '' },
dashboardLinkText: { default: __('Switch to old dashboard') },
experimentEnabled: { default: true },
},
computed: {
dropdownItems() {
return [
{ id: 0, href: this.switchDashboardPath, text: this.dashboardLinkText },
{
id: 1,
href: 'https://gitlab.com/gitlab-org/gitlab/-/issues/478844',
text: __('Provide feedback'),
extraAttrs: {
target: '__blank',
rel: 'noopener',
},
},
];
},
},
methods: {
action({ id }) {
if (id === 1) return;
this.trackEvent('toggle_merge_request_redesign', {
value: Number(this.experimentEnabled),
});
},
},
};
</script>
<template>
<gl-disclosure-dropdown
icon="preferences"
:toggle-text="__('Open action menu')"
text-sr-only
placement="bottom-end"
:items="dropdownItems"
@action="action"
/>
</template>

View File

@ -1,111 +1,150 @@
<script>
import { GlButton, GlIcon, GlAlert } from '@gitlab/ui';
import { GlButton, GlIcon, GlAlert, GlTabs, GlTab, GlLink } from '@gitlab/ui';
import MergeRequestsQuery from './merge_requests_query.vue';
import CollapsibleSection from './collapsible_section.vue';
import MergeRequest from './merge_request.vue';
import ActionDropdown from './action_dropdown.vue';
export default {
components: {
GlButton,
GlIcon,
GlAlert,
GlTabs,
GlTab,
GlLink,
MergeRequestsQuery,
CollapsibleSection,
MergeRequest,
ActionDropdown,
},
inject: ['mergeRequestsSearchDashboardPath'],
props: {
lists: {
tabs: {
type: Array,
required: true,
},
},
data() {
return {
currentTab: this.$route.params.filter || '',
};
},
methods: {
clickTab({ key }) {
this.currentTab = key;
this.$router.push({ path: key || '/' });
},
},
};
</script>
<template>
<div>
<div class="page-title-holder gl-flex">
<h1 class="page-title gl-text-size-h-display">{{ __('Merge Requests') }}</h1>
<div class="gl-ml-auto gl-self-center">
<action-dropdown />
</div>
</div>
<merge-requests-query
v-for="(list, i) in lists"
:key="`list_${i}`"
:query="list.query"
:variables="list.variables"
:class="{ 'gl-mb-4': i !== lists.length - 1 }"
>
<template #default="{ mergeRequests, count, hasNextPage, loadMore, loading, error }">
<collapsible-section :count="count" :loading="loading || error" :title="list.title">
<div>
<div class="gl-overflow-x-auto">
<table class="gl-w-full">
<colgroup>
<col style="width: 60px" />
<col style="width: 70px" />
<col style="width: 47%; min-width: 200px" />
<col style="width: 120px" />
<col style="width: 120px" />
<col style="min-width: 200px" />
</colgroup>
<thead class="gl-border-b gl-bg-gray-10">
<tr>
<th class="gl-pb-3 gl-pl-5 gl-pr-3" :aria-label="__('Pipeline status')">
<gl-icon name="pipeline" />
</th>
<th class="gl-px-3 gl-pb-3" :aria-label="__('Approvals')">
<gl-icon name="approval" />
</th>
<th class="gl-px-3 gl-pb-3 gl-text-sm gl-text-gray-700">{{ __('Title') }}</th>
<th class="gl-px-3 gl-pb-3 gl-text-center gl-text-sm gl-text-gray-700">
{{ __('Assignee') }}
</th>
<th class="gl-px-3 gl-pb-3 gl-text-center gl-text-sm gl-text-gray-700">
{{ __('Reviewers') }}
</th>
<th class="gl-pb-3 gl-pl-3 gl-pr-5 gl-text-right gl-text-sm gl-text-gray-700">
{{ __('Activity') }}
</th>
</tr>
</thead>
<tbody>
<template v-if="mergeRequests.length">
<merge-request
v-for="(mergeRequest, index) in mergeRequests"
:key="mergeRequest.id"
:merge-request="mergeRequest"
:is-last="index === mergeRequests.length - 1"
/>
</template>
<tr v-else>
<td colspan="6" :class="{ 'gl-py-6 gl-text-center': !error }">
<template v-if="loading">
{{ __('Loading...') }}
<gl-tabs>
<gl-tab
v-for="tab in tabs"
:key="tab.title"
:title="tab.title"
:active="tab.key === currentTab"
lazy
@click="clickTab(tab)"
>
<merge-requests-query
v-for="(list, i) in tab.lists"
:key="`list_${i}`"
:query="list.query"
:variables="list.variables"
:class="{ 'gl-mb-4': i !== tab.lists.length - 1 }"
>
<template #default="{ mergeRequests, count, hasNextPage, loadMore, loading, error }">
<collapsible-section :count="count" :loading="loading || error" :title="list.title">
<div>
<div class="gl-overflow-x-auto">
<table class="gl-w-full">
<colgroup>
<col style="width: 60px" />
<col style="width: 70px" />
<col style="width: 47%; min-width: 200px" />
<col style="width: 120px" />
<col style="width: 120px" />
<col style="min-width: 200px" />
</colgroup>
<thead class="gl-border-b gl-bg-gray-10">
<tr>
<th class="gl-pb-3 gl-pl-5 gl-pr-3" :aria-label="__('Pipeline status')">
<gl-icon name="pipeline" />
</th>
<th class="gl-px-3 gl-pb-3" :aria-label="__('Approvals')">
<gl-icon name="approval" />
</th>
<th class="gl-px-3 gl-pb-3 gl-text-sm gl-text-gray-700">
{{ __('Title') }}
</th>
<th class="gl-px-3 gl-pb-3 gl-text-center gl-text-sm gl-text-gray-700">
{{ __('Assignee') }}
</th>
<th class="gl-px-3 gl-pb-3 gl-text-center gl-text-sm gl-text-gray-700">
{{ __('Reviewers') }}
</th>
<th
class="gl-pb-3 gl-pl-3 gl-pr-5 gl-text-right gl-text-sm gl-text-gray-700"
>
{{ __('Activity') }}
</th>
</tr>
</thead>
<tbody>
<template v-if="mergeRequests.length">
<merge-request
v-for="(mergeRequest, index) in mergeRequests"
:key="mergeRequest.id"
:merge-request="mergeRequest"
:is-last="index === mergeRequests.length - 1"
/>
</template>
<template v-else-if="error">
<gl-alert variant="danger" :dismissible="false">
{{ __('There was an error fetching merge requests. Please try again.') }}
</gl-alert>
</template>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<template #pagination>
<div v-if="hasNextPage" class="gl-mt-4 gl-flex gl-justify-center">
<gl-button :loading="loading" data-testid="load-more" @click="loadMore">{{
__('Show more')
}}</gl-button>
</div>
<tr v-else>
<td colspan="6" :class="{ 'gl-py-6 gl-text-center': !error }">
<template v-if="loading">
{{ __('Loading...') }}
</template>
<template v-else-if="error">
<gl-alert variant="danger" :dismissible="false">
{{
__('There was an error fetching merge requests. Please try again.')
}}
</gl-alert>
</template>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<template #pagination>
<div v-if="hasNextPage" class="gl-mt-4 gl-flex gl-justify-center">
<gl-button :loading="loading" data-testid="load-more" @click="loadMore">{{
__('Show more')
}}</gl-button>
</div>
</template>
</collapsible-section>
</template>
</collapsible-section>
</merge-requests-query>
</gl-tab>
<template #tabs-end>
<li class="nav-item">
<gl-link
:href="mergeRequestsSearchDashboardPath"
class="nav-link gl-tab-nav-item !gl-no-underline"
>
{{ __('Search') }}
</gl-link>
</li>
</template>
</merge-requests-query>
</gl-tabs>
<div class="gl-mt-6 gl-text-center">
<gl-link href="https://gitlab.com/gitlab-org/gitlab/-/issues/497573">
{{ __('Leave feedback') }}
</gl-link>
</div>
</div>
</template>

View File

@ -1,13 +1,20 @@
import { concatPagination } from '@apollo/client/utilities';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import VueRouter from 'vue-router';
import createDefaultClient from '~/lib/graphql';
import App from './components/app.vue';
export function initMergeRequestDashboard(el) {
Vue.use(VueApollo);
Vue.use(VueRouter);
const { lists, switch_dashboard_path: switchDashboardPath } = JSON.parse(el.dataset.initialData);
const { tabs } = JSON.parse(el.dataset.initialData);
const router = new VueRouter({
mode: 'history',
base: el.dataset.basePath,
routes: [{ path: '/:filter' }],
});
const keyArgs = [
'state',
@ -21,6 +28,7 @@ export function initMergeRequestDashboard(el) {
return new Vue({
el,
router,
apolloProvider: new VueApollo({
defaultClient: createDefaultClient(
{},
@ -56,11 +64,11 @@ export function initMergeRequestDashboard(el) {
},
),
}),
provide: { switchDashboardPath },
provide: { mergeRequestsSearchDashboardPath: el.dataset.mergeRequestsSearchDashboardPath },
render(createElement) {
return createElement(App, {
props: {
lists,
tabs,
},
});
},

View File

@ -1,5 +1,13 @@
import { initNewResourceDropdown } from '~/vue_shared/components/new_resource_dropdown/init_new_resource_dropdown';
import { RESOURCE_TYPE_MERGE_REQUEST } from '~/vue_shared/components/new_resource_dropdown/constants';
import searchUserProjectsWithMergeRequestsEnabled from '~/vue_shared/components/new_resource_dropdown/graphql/search_user_projects_with_merge_requests_enabled.query.graphql';
import { initMergeRequestsDashboard } from './page';
initNewResourceDropdown({
resourceType: RESOURCE_TYPE_MERGE_REQUEST,
query: searchUserProjectsWithMergeRequestsEnabled,
});
const el = document.getElementById('js-merge-request-dashboard');
if (el) {

View File

@ -1,32 +1,9 @@
import Vue from 'vue';
import { __ } from '~/locale';
import addExtraTokensForMergeRequests from 'ee_else_ce/filtered_search/add_extra_tokens_for_merge_requests';
import { createFilteredSearchTokenKeys } from '~/filtered_search/issuable_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/filtered_search/constants';
import initFilteredSearch from '~/pages/search/init_filtered_search';
import { initNewResourceDropdown } from '~/vue_shared/components/new_resource_dropdown/init_new_resource_dropdown';
import { RESOURCE_TYPE_MERGE_REQUEST } from '~/vue_shared/components/new_resource_dropdown/constants';
import searchUserProjectsWithMergeRequestsEnabled from '~/vue_shared/components/new_resource_dropdown/graphql/search_user_projects_with_merge_requests_enabled.query.graphql';
import ActionDropdown from '~/merge_request_dashboard/components/action_dropdown.vue';
export const initMergeRequestsDashboard = () => {
const actionDropdownEl = document.querySelector('.js-action-dropdown');
if (actionDropdownEl) {
// eslint-disable-next-line no-new
new Vue({
el: actionDropdownEl,
provide: {
switchDashboardPath: actionDropdownEl.dataset.switchdashboardpath,
dashboardLinkText: __('Switch to new dashboard'),
experimentEnabled: false,
},
render(createElement) {
return createElement(ActionDropdown);
},
});
}
const IssuableFilteredSearchTokenKeys = createFilteredSearchTokenKeys({
disableReleaseFilter: true,
});
@ -42,9 +19,4 @@ export const initMergeRequestsDashboard = () => {
filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
useDefaultState: true,
});
initNewResourceDropdown({
resourceType: RESOURCE_TYPE_MERGE_REQUEST,
query: searchUserProjectsWithMergeRequestsEnabled,
});
};

View File

@ -14,6 +14,10 @@ export const COMPONENTS = {
import('ee_component/vue_merge_request_widget/components/checks/locked_paths.vue'),
not_approved: () =>
import('ee_component/vue_merge_request_widget/components/checks/not_approved.vue'),
security_policy_violations: () =>
import(
'ee_component/vue_merge_request_widget/components/checks/security_policy_violations.vue'
),
};
export const FAILURE_REASONS = {

View File

@ -42,22 +42,6 @@ $notification-box-shadow-color: rgba(0, 0, 0, 0.25);
}
}
.flash-alert {
background-color: $red-50;
}
.flash-notice {
background-color: $blue-50;
}
.flash-success {
background-color: $green-50;
}
.flash-warning {
background-color: $orange-50;
}
&.flash-container-no-margin {
.gl-alert {
margin-top:0;

View File

@ -4,10 +4,6 @@ $design-pin-diameter: 28px;
$design-pin-diameter-sm: 24px;
$t-gray-a-16-design-pin: rgba($black, 0.16);
.design-card-header {
background: transparent;
}
.layout-page.design-detail-layout {
max-height: 100vh;
@ -59,12 +55,6 @@ $t-gray-a-16-design-pin: rgba($black, 0.16);
height: 160px;
text-decoration: none;
.icon-version-status {
position: absolute;
right: 10px;
top: 10px;
}
&:hover {
border-color: var(--gray-400, $gray-400);
}

View File

@ -124,7 +124,6 @@ $profile-grid-flex: 1fr;
}
.ssh-keys-list {
.last-used-at,
.expires,
.key-created-at {
line-height: 32px;

View File

@ -269,7 +269,7 @@ module MergeRequestsHelper
end
def merge_request_dashboard_enabled?(current_user)
current_user.merge_request_dashboard_enabled? && !current_page?(merge_requests_search_dashboard_path)
current_user.merge_request_dashboard_enabled?
end
def sticky_header_data(project, merge_request)
@ -362,60 +362,71 @@ module MergeRequestsHelper
{ new_comment_template_paths: new_comment_template_paths(@project.group, @project).to_json }
end
def merge_request_dashboard_data(current_user)
def merge_request_dashboard_data
{
switch_dashboard_path: merge_request_dashboard_enabled?(current_user) ? merge_requests_search_dashboard_path : merge_requests_dashboard_path,
lists: [
tabs: [
{
title: _('Returned to you'),
query: 'assignedMergeRequests',
variables: {
reviewStates: %w[REVIEWED REQUESTED_CHANGES]
}
title: _('Needs attention'),
key: '',
lists: [
{
title: _('Returned to you'),
query: 'assignedMergeRequests',
variables: {
reviewStates: %w[REVIEWED REQUESTED_CHANGES]
}
},
{
title: _('Reviews requested'),
query: 'reviewRequestedMergeRequests',
variables: {
reviewStates: %w[UNAPPROVED UNREVIEWED REVIEW_STARTED]
}
},
{
title: _('Assigned to you'),
query: 'assignedMergeRequests',
variables: {
reviewerWildcardId: 'NONE'
}
}
]
},
{
title: _('Reviews requested'),
query: 'reviewRequestedMergeRequests',
variables: {
reviewStates: %w[UNAPPROVED UNREVIEWED REVIEW_STARTED]
}
},
{
title: _('Assigned to you'),
query: 'assignedMergeRequests',
variables: {
reviewerWildcardId: 'NONE'
}
},
{
title: _('Waiting for others'),
query: 'assigneeOrReviewerMergeRequests',
variables: {
reviewerReviewStates: %w[REVIEWED REQUESTED_CHANGES],
assignedReviewStates: %w[UNREVIEWED UNAPPROVED REVIEW_STARTED]
}
},
{
title: _('Approved by you'),
query: 'reviewRequestedMergeRequests',
variables: {
reviewState: 'APPROVED'
}
},
{
title: _('Approved by others'),
query: 'assignedMergeRequests',
variables: {
reviewState: 'APPROVED'
}
},
{
title: _('Merged recently'),
query: 'assigneeOrReviewerMergeRequests',
variables: {
state: 'merged',
mergedAfter: 2.weeks.ago.to_time.iso8601
}
title: _('Following'),
key: 'following',
lists: [
{
title: _('Waiting for others'),
query: 'assigneeOrReviewerMergeRequests',
variables: {
reviewerReviewStates: %w[REVIEWED REQUESTED_CHANGES],
assignedReviewStates: %w[UNREVIEWED UNAPPROVED REVIEW_STARTED]
}
},
{
title: _('Approved by you'),
query: 'reviewRequestedMergeRequests',
variables: {
reviewState: 'APPROVED'
}
},
{
title: _('Approved by others'),
query: 'assignedMergeRequests',
variables: {
reviewState: 'APPROVED'
}
},
{
title: _('Merged recently'),
query: 'assigneeOrReviewerMergeRequests',
variables: {
state: 'merged',
mergedAfter: 2.weeks.ago.to_time.iso8601
}
}
]
}
]
}

View File

@ -75,7 +75,7 @@ module ResourceAccessTokens
end
def delete_failed_user(user)
DeleteUserWorker.perform_async(current_user.id, user.id, hard_delete: true, skip_authorization: true)
DeleteUserWorker.perform_async(current_user.id, user.id, hard_delete: true, skip_authorization: true, reason_for_deletion: "Access token creation failed")
end
def default_user_params

View File

@ -38,7 +38,7 @@ module ResourceAccessTokens
attr_reader :current_user, :access_token, :bot_user, :resource
def destroy_bot_user
DeleteUserWorker.perform_async(current_user.id, bot_user.id, skip_authorization: true)
DeleteUserWorker.perform_async(current_user.id, bot_user.id, skip_authorization: true, reason_for_deletion: "Access token revoked")
end
def can_destroy_token?

View File

@ -58,6 +58,9 @@ module Users
# Load the records. Groups are unavailable after membership is destroyed.
solo_owned_groups = user.solo_owned_groups.load
# Load the project_bot user resource. It is unavailable after membership is destroyed.
options[:project_bot_resource] ||= user.resource_bot_resource
user.members.each_batch { |batch| batch.destroy_all } # rubocop:disable Cop/DestroyAll
solo_owned_groups.each do |group|

View File

@ -8,26 +8,37 @@
= render_if_exists 'shared/dashboard/saml_reauth_notice',
groups_requiring_saml_reauth: user_groups_requiring_reauth
- if merge_request_dashboard_enabled?(current_user)
#js-merge-request-dashboard{ data: { initial_data: merge_request_dashboard_data(current_user).to_json } }
= render ::Layouts::PageHeadingComponent.new(_('Merge requests'))
= render ::Layouts::PageHeadingComponent.new(title) do |c|
- c.with_actions do
- if current_user
= render 'shared/new_project_item_vue_select'
- if merge_request_dashboard_enabled?(current_user) && !current_page?(merge_requests_search_dashboard_path)
#js-merge-request-dashboard{ data: { base_path: merge_requests_dashboard_path, merge_requests_search_dashboard_path: merge_requests_search_dashboard_path, initial_data: merge_request_dashboard_data.to_json } }
= gl_loading_icon(size: 'lg')
- else
= render ::Layouts::PageHeadingComponent.new(title) do |c|
- c.with_actions do
- if current_user
= render 'shared/new_project_item_vue_select'
- if current_user&.merge_request_dashboard_enabled?
.js-action-dropdown{ data: { switchDashboardPath: merge_requests_dashboard_path } }
.top-area
= render 'shared/issuable/nav', type: :merge_requests, display_count: !(@no_filters_set || @search_timeout_occurred)
- if !merge_request_dashboard_enabled?(current_user) || current_page?(merge_requests_search_dashboard_path)
- if merge_request_dashboard_enabled?(current_user)
= gl_tabs_nav do
= gl_tab_link_to _('Needs attention'), merge_requests_dashboard_path
= gl_tab_link_to _('Following'), merge_requests_following_dashboard_path
= gl_tab_link_to _('Search'), merge_requests_search_dashboard_path, item_active: true
= render 'shared/issuable/search_bar',
type: :merge_requests,
disable_target_branch: true,
disable_releases: true,
disable_environments: true
%div{ class: "#{'gl-bg-gray-10' if merge_request_dashboard_enabled?(current_user)}" }
- unless merge_request_dashboard_enabled?(current_user)
.top-area
= render 'shared/issuable/nav', type: :merge_requests, display_count: !(@no_filters_set || @search_timeout_occurred)
= render 'shared/issuable/search_bar',
type: :merge_requests,
disable_target_branch: true,
disable_releases: true,
disable_environments: true,
filtered_search_class: merge_request_dashboard_enabled?(current_user) ? '!gl-border-b-0' : ''
- if merge_request_dashboard_enabled?(current_user)
.top-area
= render 'shared/issuable/nav', type: :merge_requests, display_count: !(@no_filters_set || @search_timeout_occurred)
- if current_user && @no_filters_set
= render 'no_filter_selected'

View File

@ -1,7 +1,6 @@
- message = read_only_message
- if message
.flash-container.flash-container-page
.flash-notice
%div{ class: container_class }
%span
= message
= render Pajamas::AlertComponent.new(variant: :info, alert_options: { data: { testid: 'read-only-banner' } }) do |c|
- c.with_body do
= message

View File

@ -45,7 +45,7 @@
= render partial: 'projects/commit/signature', object: @tag.signature
- if can?(current_user, :admin_tag, @project)
= render 'edit_release_button', tag: @tag, project: @project, release: @release
= link_button_to nil, project_tree_path(@project, @tag.name), class: 'has-tooltip', title: s_('TagsPage|Browse files'), icon: 'folder-open'
= link_button_to nil, project_tree_path(@project, @tag.name, ref_type: :tags), class: 'has-tooltip', title: s_('TagsPage|Browse files'), icon: 'folder-open'
= link_button_to nil, project_commits_path(@project, @tag.name), class: 'has-tooltip', title: s_('TagsPage|Browse commits'), icon: 'history'
= render 'projects/buttons/download', project: @project, ref: @tag.name
- if can?(current_user, :admin_tag, @project)

View File

@ -3,11 +3,12 @@
- disable_target_branch = local_assigns.fetch(:disable_target_branch, false)
- disable_releases = local_assigns.fetch(:disable_releases, false)
- disable_environments = local_assigns.fetch(:disable_environments, false)
- filtered_search_class = local_assigns.fetch(:filtered_search_class, '')
- placeholder = local_assigns[:placeholder] || _('Search or filter results…')
- block_css_class = type != :productivity_analytics ? 'row-content-block second-block' : ''
.issues-filters
.issues-details-filters.filtered-search-block.gl-flex.gl-flex-col.lg:gl-flex-row.gl-gap-3{ class: block_css_class }
.issues-details-filters.filtered-search-block.gl-flex.gl-flex-col.lg:gl-flex-row.gl-gap-3{ class: [block_css_class, filtered_search_class] }
.gl-flex.gl-flex-col.md:gl-flex-row.gl-grow.gl-w-full
= form_tag page_filter_path, method: :get, class: 'filter-form js-filter-form gl-w-full' do
- if params[:search].present?

View File

@ -2,8 +2,10 @@
- if display_issues_count_warning?(@milestone)
.flash-container
.flash-warning#milestone-issue-count-warning
= milestone_issues_count_message(@milestone)
= render Pajamas::AlertComponent.new(variant: :warning,
alert_options: { id: 'milestone-issue-count-warning' }) do |c|
- c.with_body do
= milestone_issues_count_message(@milestone)
.row.gl-mt-3
.col-md-4

View File

@ -54,7 +54,11 @@ class RemoveExpiredMembersWorker # rubocop:disable Scalability/IdempotentWorker
expired_user = member.user
if expired_user.project_bot?
Users::DestroyService.new(nil).execute(expired_user, skip_authorization: true)
Users::DestroyService.new(nil).execute(expired_user, {
skip_authorization: true,
project_bot_resource: member.source,
reason_for_deletion: "Membership expired"
})
end
@updated_count += 1

View File

@ -38,7 +38,12 @@ module ResourceAccessTokens
DeleteUserWorker.bulk_perform_async_with_contexts(
users,
arguments_proc: ->(user) { [admin_bot_id, user.id, { skip_authorization: true }] },
arguments_proc: ->(user) {
[
admin_bot_id, user.id,
{ skip_authorization: true, reason_for_deletion: "No active token assigned" }
]
},
context_proc: ->(user) { { user: user } }
)
end

View File

@ -5,6 +5,7 @@ resource :dashboard, controller: 'dashboard', only: [] do
get :issues
get :merge_requests
get :activity
get 'merge_requests/following', to: 'dashboard#merge_requests'
get 'merge_requests/search', to: 'dashboard#search_merge_requests'
scope module: :dashboard do

View File

@ -41,6 +41,8 @@ const {
WEBPACK_OUTPUT_PATH,
WEBPACK_PUBLIC_PATH,
SOURCEGRAPH_PUBLIC_PATH,
PDF_JS_WORKER_PUBLIC_PATH,
PDF_JS_CMAPS_PUBLIC_PATH,
GITLAB_WEB_IDE_PUBLIC_PATH,
copyFilesPatterns,
} = require('./webpack.constants');
@ -845,6 +847,8 @@ module.exports = {
// This is used by Sourcegraph because these assets are loaded dnamically
'process.env.SOURCEGRAPH_PUBLIC_PATH': JSON.stringify(SOURCEGRAPH_PUBLIC_PATH),
'process.env.GITLAB_WEB_IDE_PUBLIC_PATH': JSON.stringify(GITLAB_WEB_IDE_PUBLIC_PATH),
'process.env.PDF_JS_WORKER_PUBLIC_PATH': JSON.stringify(PDF_JS_WORKER_PUBLIC_PATH),
'process.env.PDF_JS_CMAPS_PUBLIC_PATH': JSON.stringify(PDF_JS_CMAPS_PUBLIC_PATH),
'window.IS_VITE': JSON.stringify(false),
...(IS_PRODUCTION ? {} : { LIVE_RELOAD: DEV_SERVER_LIVERELOAD }),
}),

View File

@ -15,20 +15,31 @@ const GITLAB_WEB_IDE_PATH = path.join('gitlab-vscode', GITLAB_WEB_IDE_VERSION, '
const GITLAB_WEB_IDE_OUTPUT_PATH = path.join(WEBPACK_OUTPUT_PATH, GITLAB_WEB_IDE_PATH);
const GITLAB_WEB_IDE_PUBLIC_PATH = path.join(WEBPACK_PUBLIC_PATH, GITLAB_WEB_IDE_PATH);
const PDF_JS_VERSION = require('pdfjs-dist/package.json').version;
const PDF_JS_WORKER_FILE_PATH = '/legacy/build/pdf.worker.min.js';
const PDF_JS_WORKER_PATH = path.join('pdfjs', PDF_JS_VERSION, PDF_JS_WORKER_FILE_PATH);
const PDF_JS_WORKER_OUTPUT_PATH = path.join(WEBPACK_OUTPUT_PATH, PDF_JS_WORKER_PATH);
const PDF_JS_WORKER_PUBLIC_PATH = path.join(WEBPACK_PUBLIC_PATH, PDF_JS_WORKER_PATH);
const PDF_JS_CMAPS_PATH = path.join('pdfjs', PDF_JS_VERSION, 'cmaps/');
const PDF_JS_CMAPS_OUTPUT_PATH = path.join(WEBPACK_OUTPUT_PATH, PDF_JS_CMAPS_PATH);
const PDF_JS_CMAPS_PUBLIC_PATH = path.join(WEBPACK_PUBLIC_PATH, PDF_JS_CMAPS_PATH);
const IS_EE = require('./helpers/is_ee_env');
const IS_JH = require('./helpers/is_jh_env');
const SOURCEGRAPH_PACKAGE = '@sourcegraph/code-host-integration';
const GITLAB_WEB_IDE_PACKAGE = '@gitlab/web-ide';
const PDFJS_PACKAGE = 'pdfjs-dist';
const copyFilesPatterns = [
{
from: path.join(ROOT_PATH, 'node_modules/pdfjs-dist/cmaps/'),
to: path.join(WEBPACK_OUTPUT_PATH, 'pdfjs/cmaps/'),
from: path.join(ROOT_PATH, 'node_modules', PDFJS_PACKAGE, 'cmaps'),
to: PDF_JS_CMAPS_OUTPUT_PATH,
},
{
from: path.join(ROOT_PATH, 'node_modules/pdfjs-dist/legacy/build/pdf.worker.min.js'),
to: path.join(WEBPACK_OUTPUT_PATH, 'pdfjs/'),
from: path.join(ROOT_PATH, 'node_modules', PDFJS_PACKAGE, PDF_JS_WORKER_FILE_PATH),
to: PDF_JS_WORKER_OUTPUT_PATH,
},
{
from: path.join(ROOT_PATH, 'node_modules', SOURCEGRAPH_PACKAGE, '/'),
@ -49,6 +60,8 @@ module.exports = {
ROOT_PATH,
WEBPACK_OUTPUT_PATH,
WEBPACK_PUBLIC_PATH,
PDF_JS_WORKER_PUBLIC_PATH,
PDF_JS_CMAPS_PUBLIC_PATH,
SOURCEGRAPH_PUBLIC_PATH,
GITLAB_WEB_IDE_PUBLIC_PATH,
copyFilesPatterns,

View File

@ -0,0 +1,34 @@
# frozen_string_literal: true
class AddSyncCorrectWorkItemTypeIdTriggerToIssues < Gitlab::Database::Migration[2.2]
milestone '17.5'
include Gitlab::Database::SchemaHelpers
ISSUES_TABLE = 'issues'
WORK_ITEM_TYPES_TABLE = 'work_item_types'
TRIGGER_FUNCTION_NAME = 'update_issue_correct_work_item_type_id_sync_event'
TRIGGER_NAME = 'trigger_correct_work_item_type_id_sync_event_on_issue_update'
def up
create_trigger_function(TRIGGER_FUNCTION_NAME, replace: true) do
<<~SQL
SELECT "correct_id"
INTO NEW."correct_work_item_type_id"
FROM "#{WORK_ITEM_TYPES_TABLE}"
WHERE "#{WORK_ITEM_TYPES_TABLE}"."id" = NEW."work_item_type_id";
RETURN NEW;
SQL
end
create_trigger(
ISSUES_TABLE, TRIGGER_NAME, TRIGGER_FUNCTION_NAME, fires: 'BEFORE INSERT OR UPDATE of work_item_type_id'
)
end
def down
drop_trigger(ISSUES_TABLE, TRIGGER_NAME)
drop_function(TRIGGER_FUNCTION_NAME)
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
class AddUniqueConstraintToWorkItemTypesCorrectId < Gitlab::Database::Migration[2.2]
INDEX_NAME = 'index_work_item_types_on_correct_id_unique'
disable_ddl_transaction!
milestone '17.5'
def up
add_concurrent_index :work_item_types, :correct_id, unique: true, name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :work_item_types, INDEX_NAME
end
end

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
class AddFkToIssuesCorrectWorkItemTypeId < Gitlab::Database::Migration[2.2]
milestone '17.5'
disable_ddl_transaction!
def up
add_concurrent_foreign_key :issues,
:work_item_types,
column: :correct_work_item_type_id,
target_column: :correct_id,
validate: false,
on_delete: nil
end
def down
with_lock_retries do
remove_foreign_key_if_exists :issues, column: :correct_work_item_type_id
end
end
end

View File

@ -0,0 +1 @@
150184cdc3da0f4f7919ce101f577542b3acbd65958b1ed4826364183247e40e

View File

@ -0,0 +1 @@
8f38307f2ffee7553c46c188dbf18af57a28d306811b75e106bfc83b15df5f05

View File

@ -0,0 +1 @@
eff86eb6c3d6002da8128edf071104bd2be7a88f2c89d477b24d1b8387aa9ee5

View File

@ -2670,6 +2670,19 @@ BEGIN
END
$$;
CREATE FUNCTION update_issue_correct_work_item_type_id_sync_event() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
SELECT "correct_id"
INTO NEW."correct_work_item_type_id"
FROM "work_item_types"
WHERE "work_item_types"."id" = NEW."work_item_type_id";
RETURN NEW;
END
$$;
CREATE FUNCTION update_location_from_vulnerability_occurrences() RETURNS trigger
LANGUAGE plpgsql
AS $$
@ -31651,6 +31664,8 @@ CREATE INDEX index_work_item_type_custom_fields_on_work_item_type_id ON work_ite
CREATE INDEX index_work_item_types_on_base_type_and_id ON work_item_types USING btree (base_type, id);
CREATE UNIQUE INDEX index_work_item_types_on_correct_id_unique ON work_item_types USING btree (correct_id);
CREATE UNIQUE INDEX index_work_item_types_on_name_unique ON work_item_types USING btree (TRIM(BOTH FROM lower(name)));
CREATE UNIQUE INDEX index_work_item_widget_definitions_on_type_id_and_name ON work_item_widget_definitions USING btree (work_item_type_id, TRIM(BOTH FROM lower(name)));
@ -33821,6 +33836,8 @@ CREATE TRIGGER trigger_catalog_resource_sync_event_on_project_update AFTER UPDAT
CREATE TRIGGER trigger_cf646a118cbb BEFORE INSERT OR UPDATE ON milestone_releases FOR EACH ROW EXECUTE FUNCTION trigger_cf646a118cbb();
CREATE TRIGGER trigger_correct_work_item_type_id_sync_event_on_issue_update BEFORE INSERT OR UPDATE OF work_item_type_id ON issues FOR EACH ROW EXECUTE FUNCTION update_issue_correct_work_item_type_id_sync_event();
CREATE TRIGGER trigger_d4487a75bd44 BEFORE INSERT OR UPDATE ON terraform_state_versions FOR EACH ROW EXECUTE FUNCTION trigger_d4487a75bd44();
CREATE TRIGGER trigger_d5c895007948 BEFORE INSERT OR UPDATE ON protected_environment_approval_rules FOR EACH ROW EXECUTE FUNCTION trigger_d5c895007948();
@ -34089,6 +34106,9 @@ ALTER TABLE ONLY analytics_devops_adoption_segments
ALTER TABLE ONLY project_statistics
ADD CONSTRAINT fk_198ad46fdc FOREIGN KEY (root_namespace_id) REFERENCES namespaces(id) ON DELETE SET NULL;
ALTER TABLE ONLY issues
ADD CONSTRAINT fk_1adaba52b0 FOREIGN KEY (correct_work_item_type_id) REFERENCES work_item_types(correct_id) NOT VALID;
ALTER TABLE ONLY approval_policy_rule_project_links
ADD CONSTRAINT fk_1c78796d52 FOREIGN KEY (approval_policy_rule_id) REFERENCES approval_policy_rules(id) ON DELETE CASCADE;

View File

@ -114,19 +114,6 @@ Find the GitLab official Docker image at:
Use the image tag that corresponds to your GitLab version. For example, if the
GitLab version is `v17.5.0`, use `self-hosted-v17.5.0-ee` tag.
#### Set up the volumes location
Create a directory where the logs will reside on the Docker host. It can be under
your user's home directory (for example `~/gitlab-agw`), or in a directory like
`/srv/gitlab-agw`. To create that directory, run:
```shell
sudo mkdir -p /srv/gitlab-agw
```
If you're running Docker with a user other than `root`, ensure appropriate
permissions have been granted to that directory.
#### Start a container from the image
For Docker images with version `self-hosted-17.4.0-ee` and later, run the following:
@ -244,6 +231,72 @@ To upgrade the AI Gateway, download the newest Docker image tag.
1. Ensure that the environment variables are all set correctly.
## Enable logging
Prerequisites:
- You must be an administrator for your self-managed instance.
To enable logging and access the logs, enable the feature flag:
```ruby
Feature.enable(:expanded_ai_logging)
```
Disabling the feature flag stops logs from being written.
### Logs in your GitLab installation
In your instance log directory, a file called `llm.log` is populated.
For more information on:
- Logged events and their properties, see the [logged event documentation](../../development/ai_features/logged_events.md).
- How to rotate, manage, export and visualize the logs in `llm.log`, see the [log system documentation](../logs/index.md).
### Logs in your AI Gateway container
To specify the location of logs generated by AI Gateway, run:
```shell
docker run -e AIGW_GITLAB_URL=<your_gitlab_instance> \
-e AIGW_GITLAB_API_URL=https://<your_gitlab_domain>/api/v4/ \
-e AIGW_GITLAB_API_URL=https://<your_gitlab_domain>/api/v4/ \
-e AIGW_LOGGING__TO_FILE="aigateway.log" \
-v <your_file_path>:"aigateway.log"
<image>
```
If you do not specify a file name, logs are streamed to the output.
Additionally, the outputs of the AI Gateway execution can also be useful for debugging issues. To access them:
- When using Docker:
```shell
docker logs <container-id>
```
- When using Kubernetes:
```shell
kubectl logs <container-name>
```
To ingest these logs into the logging solution, see your logging provider documentation.
### Logs in your inference service provier
GitLab does not manage logs generated by your inference service provider. Please refer to the documentation of your inference service
provider on how to use their logs.
### Cross-referencing logs between AI Gateway and GitLab
The property `correlation_id` is assigned to every request and is carried across different components that respond to a
request. For more information, see the [documentation on finding logs with a correlation ID](../logs/tracing_correlation_id.md).
Correlation ID is not available in your model provider logs.
## Alternative installation methods
For information on alternative ways to install the AI Gateway, see

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -31,10 +31,8 @@ To modify this setting:
1. On the left sidebar, at the bottom, select **Admin**.
1. Select **Settings > Network**.
1. Expand **Performance optimization**.
1. Edit the **Push event activities limit** setting.
1. Edit the **Push event activities limit** setting.
- Through the [Application settings API](../../api/settings.md#list-of-settings-that-can-be-accessed-via-api-calls)
as `push_event_activities_limit`.
The default value is `3`, but the value can be greater than or equal to `0`. Setting this value to `0` does not disable throttling.
![The push event activities limit set to 3.](img/push_event_activities_limit_v12_4.png)

View File

@ -12365,6 +12365,29 @@ The edge type for [`CodeQualityDegradation`](#codequalitydegradation).
| <a id="codequalitydegradationedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="codequalitydegradationedgenode"></a>`node` | [`CodeQualityDegradation`](#codequalitydegradation) | The item at the end of the edge. |
#### `CodeSuggestionEventConnection`
The connection type for [`CodeSuggestionEvent`](#codesuggestionevent).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="codesuggestioneventconnectionedges"></a>`edges` | [`[CodeSuggestionEventEdge]`](#codesuggestioneventedge) | A list of edges. |
| <a id="codesuggestioneventconnectionnodes"></a>`nodes` | [`[CodeSuggestionEvent]`](#codesuggestionevent) | A list of nodes. |
| <a id="codesuggestioneventconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
#### `CodeSuggestionEventEdge`
The edge type for [`CodeSuggestionEvent`](#codesuggestionevent).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="codesuggestioneventedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="codesuggestioneventedgenode"></a>`node` | [`CodeSuggestionEvent`](#codesuggestionevent) | The item at the end of the edge. |
#### `CommitConnection`
The connection type for [`Commit`](#commit).
@ -17792,6 +17815,14 @@ Self-hosted LLM servers.
| <a id="aiselfhostedmodelname"></a>`name` | [`String!`](#string) | Deployment name of the self-hosted model. |
| <a id="aiselfhostedmodelupdatedat"></a>`updatedAt` | [`Time`](#time) | Timestamp of last update. |
### `AiUsageData`
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="aiusagedatacodesuggestionevents"></a>`codeSuggestionEvents` | [`CodeSuggestionEventConnection`](#codesuggestioneventconnection) | Events related to code suggestions feature. (see [Connections](#connections)) |
### `AlertManagementAlert`
Describes an alert from the project's Alert Management.
@ -19897,6 +19928,19 @@ Code Quality report for a pipeline.
| <a id="codequalityreportsummaryminor"></a>`minor` | [`Int`](#int) | Total number of minor status. |
| <a id="codequalityreportsummaryunknown"></a>`unknown` | [`Int`](#int) | Total number of unknown status. |
### `CodeSuggestionEvent`
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="codesuggestioneventevent"></a>`event` | [`AiUsageCodeSuggestionEvent!`](#aiusagecodesuggestionevent) | Type of the event. |
| <a id="codesuggestioneventlanguage"></a>`language` | [`String`](#string) | Programming language in the context of the suggestion. |
| <a id="codesuggestioneventsuggestionsize"></a>`suggestionSize` | [`String`](#string) | Size of the code suggestion. |
| <a id="codesuggestioneventtimestamp"></a>`timestamp` | [`Time!`](#time) | When the event happened. |
| <a id="codesuggestioneventuniquetrackingid"></a>`uniqueTrackingId` | [`String`](#string) | Unique tracking number. |
| <a id="codesuggestioneventuser"></a>`user` | [`UserCore!`](#usercore) | User associated with the event. |
### `CodequalityReportsComparer`
Represents reports comparison for code quality.
@ -23308,6 +23352,7 @@ GPG signature for a signed commit.
| <a id="groupactualrepositorysizelimit"></a>`actualRepositorySizeLimit` | [`Float`](#float) | Size limit for repositories in the namespace in bytes. This limit only applies to namespaces under Project limit enforcement. |
| <a id="groupactualsizelimit"></a>`actualSizeLimit` | [`Float`](#float) | The actual storage size limit (in bytes) based on the enforcement type of either repository or namespace. This limit is agnostic of enforcement type. |
| <a id="groupadditionalpurchasedstoragesize"></a>`additionalPurchasedStorageSize` | [`Float`](#float) | Additional storage purchased for the root namespace in bytes. |
| <a id="groupaiusagedata"></a>`aiUsageData` **{warning-solid}** | [`AiUsageData`](#aiusagedata) | **Introduced** in GitLab 17.5. **Status**: Experiment. AI-related data. |
| <a id="groupallowstalerunnerpruning"></a>`allowStaleRunnerPruning` | [`Boolean!`](#boolean) | Indicates whether to regularly prune stale group runners. Defaults to false. |
| <a id="groupamazons3configurations"></a>`amazonS3Configurations` | [`AmazonS3ConfigurationTypeConnection`](#amazons3configurationtypeconnection) | Amazon S3 configurations that receive audit events belonging to the group. (see [Connections](#connections)) |
| <a id="groupautodevopsenabled"></a>`autoDevopsEnabled` | [`Boolean`](#boolean) | Indicates whether Auto DevOps is enabled for all projects within this group. |
@ -29717,6 +29762,7 @@ Project-level settings for product analytics provider.
| <a id="projectactualrepositorysizelimit"></a>`actualRepositorySizeLimit` | [`Float`](#float) | Size limit for the repository in bytes. |
| <a id="projectagentconfigurations"></a>`agentConfigurations` | [`AgentConfigurationConnection`](#agentconfigurationconnection) | Agent configurations defined by the project. (see [Connections](#connections)) |
| <a id="projectaiagents"></a>`aiAgents` **{warning-solid}** | [`AiAgentConnection`](#aiagentconnection) | **Introduced** in GitLab 16.9. **Status**: Experiment. Ai Agents for the project. |
| <a id="projectaiusagedata"></a>`aiUsageData` **{warning-solid}** | [`AiUsageData`](#aiusagedata) | **Introduced** in GitLab 17.5. **Status**: Experiment. AI-related data. |
| <a id="projectallowmergeonskippedpipeline"></a>`allowMergeOnSkippedPipeline` | [`Boolean`](#boolean) | If `only_allow_merge_if_pipeline_succeeds` is true, indicates if merge requests of the project can also be merged with skipped jobs. |
| <a id="projectallowsmultiplemergerequestassignees"></a>`allowsMultipleMergeRequestAssignees` | [`Boolean!`](#boolean) | Project allows assigning multiple users to a merge request. |
| <a id="projectallowsmultiplemergerequestreviewers"></a>`allowsMultipleMergeRequestReviewers` | [`Boolean!`](#boolean) | Project allows assigning multiple reviewers to a merge request. |
@ -36133,6 +36179,16 @@ Types of messages returned from AI features.
| ----- | ----------- |
| <a id="aimessagetypetool"></a>`TOOL` | Tool selection message. |
### `AiUsageCodeSuggestionEvent`
Type of code suggestion event.
| Value | Description |
| ----- | ----------- |
| <a id="aiusagecodesuggestioneventcode_suggestion_accepted_in_ide"></a>`CODE_SUGGESTION_ACCEPTED_IN_IDE` | Code suggestion accepted. |
| <a id="aiusagecodesuggestioneventcode_suggestion_rejected_in_ide"></a>`CODE_SUGGESTION_REJECTED_IN_IDE` | Code suggestion rejected. |
| <a id="aiusagecodesuggestioneventcode_suggestion_shown_in_ide"></a>`CODE_SUGGESTION_SHOWN_IN_IDE` | Code suggestion shown. |
### `AlertManagementAlertSort`
Values for sorting alerts.

View File

@ -171,6 +171,7 @@ these parameters:
- `duo_features_enabled`
- `lock_duo_features_enabled`
- `use_clickhouse_for_analytics`
- `pre_receive_secret_detection_enabled`
```json
{
@ -703,6 +704,7 @@ listed in the descriptions of the relevant settings.
| `lock_duo_features_enabled` | boolean | no | Indicates whether the GitLab Duo features enabled setting is enforced for all subgroups. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/144931) in GitLab 16.10. Self-managed, Premium and Ultimate only. |
| `nuget_skip_metadata_url_validation` | boolean | no | Indicates whether to skip metadata URL validation for the NuGet package. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/145887) in GitLab 17.0. |
| `require_admin_two_factor_authentication` | boolean | no | Allow administrators to require 2FA for all administrators on the instance. |
| `pre_receive_secret_detection_enabled` | boolean | no | Allow projects to enable secret push protection. This does not enable secret push protection. When you enable this feature, you accept the [GitLab Testing Agreement](https://handbook.gitlab.com/handbook/legal/testing-agreement/). Ultimate only. |
### Configure inactive project deletion

View File

@ -23,7 +23,7 @@ When you rebase:
1. Git stacks the commits you have in your branch on top of all
the commits it imported from that branch:
![Git rebase illustration](img/git_rebase_v13_5.png)
![Git rebase illustration](img/git_rebase_illustration.png)
While most rebases are performed against `main`, you can rebase against any other
branch, such as `release-15-3`. You can also specify a different remote repository

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

13
doc/topics/gitlab_flow.md Normal file
View File

@ -0,0 +1,13 @@
---
redirect_to: 'https://about.gitlab.com/blog/2023/07/27/gitlab-flow-duo/'
remove_date: '2025-10-08'
---
<!-- markdownlint-disable -->
This document was moved to [another location](https://about.gitlab.com/blog/2023/07/27/gitlab-flow-duo/).
<!-- This redirect file can be deleted after <2025-10-08>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -570,7 +570,7 @@ Audit event types belong to the following product categories.
| [`user_blocked`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113784) | Event triggered when a user is blocked | **{check-circle}** Yes | **{dotted-circle}** No | GitLab [15.11](https://gitlab.com/gitlab-org/gitlab/-/issues/374107) | User |
| [`user_created`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113784) | Event triggered when a user is created | **{check-circle}** Yes | **{check-circle}** Yes | GitLab [15.10](https://gitlab.com/gitlab-org/gitlab/-/issues/374107) | User |
| [`user_deactivate`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117776) | Event triggered on user deactivate action | **{check-circle}** Yes | **{check-circle}** Yes | GitLab [16.0](https://gitlab.com/gitlab-org/gitlab/-/issues/13473) | User |
| [`user_destroyed`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113784) | Event triggered when a user is scheduled for removal from the instance | **{check-circle}** Yes | **{dotted-circle}** No | GitLab [15.11](https://gitlab.com/gitlab-org/gitlab/-/issues/374107) | User |
| [`user_destroyed`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113784) | Event triggered when a user is scheduled for removal from the instance | **{check-circle}** Yes | **{dotted-circle}** No | GitLab [15.11](https://gitlab.com/gitlab-org/gitlab/-/issues/374107) | User, Group, Project |
| [`user_email_changed_and_user_signed_in`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106090) | audit when user emailed changed and user signed in | **{check-circle}** Yes | **{check-circle}** Yes | GitLab [15.8](https://gitlab.com/gitlab-org/gitlab/-/issues/369331) | User |
| [`user_impersonation`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79340) | Triggered when an instance administrator starts or stops impersonating a user | **{check-circle}** Yes | **{check-circle}** Yes | GitLab [14.8](https://gitlab.com/gitlab-org/gitlab/-/issues/300961) | User, Group |
| [`user_password_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106086) | audit when user password is updated | **{check-circle}** Yes | **{check-circle}** Yes | GitLab [15.7](https://gitlab.com/gitlab-org/gitlab/-/issues/369330) | User |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -233,9 +233,9 @@ internal/README.md @user2
```
Each Code Owner in the merge request widget is listed under a label.
The following image shows **Groups** and **Documentation** sections:
The following image shows **Default**, **Frontend**, and **Technical Writing** sections:
![MR widget - Sectional Code Owners](../img/sectional_code_owners_v13.2.png)
![MR widget - Sectional Code Owners](../img/sectional_code_owners_v17_4.png)
#### Set default owner for a section
@ -365,7 +365,7 @@ In this example, the `[Go]` section is optional:
The optional Code Owners section displays in merge requests under the description:
![MR widget - Optional Code Owners sections](../img/optional_code_owners_sections_v13_8.png)
![MR widget - Optional Code Owners sections](../img/optional_code_owners_sections_v17_4.png)
If a section is duplicated in the file, and one of them is marked as optional and the other isn't, the section is required.

View File

@ -172,7 +172,7 @@ and the files' IDs.
On the repository file tree, GitLab displays an LFS badge for files
tracked by Git LFS plus a padlock icon on exclusively-locked files:
![LFS-Locked files](img/lfs_locked_files_v13_2.png)
![LFS-Locked files](img/lfs_locked_files_v17_4.png)
You can also [view and remove existing locks](#view-and-remove-existing-locks) from the GitLab UI.

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -31,10 +31,7 @@ can access the object pool connected to the source project.
To fork an existing project in GitLab:
1. On the project's homepage, in the upper-right corner, select **Fork** (**{fork}**):
![Fork this project](img/forking_workflow_fork_button_v13_10.png)
1. On the project's homepage, in the upper-right corner, select **Fork** (**{fork}**).
1. Optional. Edit the **Project name**.
1. For **Project URL**, select the [namespace](../../namespace/index.md)
your fork should belong to.

View File

@ -148,7 +148,7 @@ subscription levels, and [groups](project/wiki/group.md) for
Snippets with multiple files display a file count in the [snippet list](https://gitlab.com/dashboard/snippets):
![Example of snippet](img/snippet_tooltip_v13_10.png)
![Example of snippet](img/snippet_tooltip_v17_4.png)
You can manage snippets with Git (because they're [versioned](#versioned-snippets)
by a Git repository), through the [Snippets API](../api/snippets.md), and in the GitLab UI.

View File

@ -273,16 +273,8 @@ module Gitlab
def query_and_release(...)
if low_timeout_for_host_queries?
Gitlab::Database::LoadBalancing::Logger.info(
event: :host_query_low_statement_timeout,
message: "Using low statement timeout code path for host query"
)
query_and_release_fast_timeout(...)
else
Gitlab::Database::LoadBalancing::Logger.info(
event: :host_query_high_statement_timeout,
message: "Using high statement timeout code path for host query"
)
query_and_release_old(...)
end
end
@ -336,7 +328,7 @@ module Gitlab
end
def low_timeout_for_host_queries?
Feature.enabled?(:load_balancer_low_statement_timeout, Feature.current_pod)
Feature.enabled?(:load_balancer_low_statement_timeout)
end
end
end

View File

@ -9,6 +9,10 @@ module Gitlab
JobReplicaNotUpToDate = Class.new(::Gitlab::SidekiqMiddleware::RetryError)
REPLICA_WAIT_SLEEP_SECONDS = 0.5
URGENT_REPLICA_WAIT_SLEEP_SECONDS = 0.1
SLEEP_ATTEMPTS = 3
URGENT_SLEEP_ATTEMPTS = 5
def call(worker, job, _queue)
# ActiveJobs have wrapped class stored in 'wrapped' key
@ -54,8 +58,8 @@ module Gitlab
# Happy case: we can read from a replica.
return replica_strategy(worker_class, job) if databases_in_sync?(wal_locations)
3.times do
sleep REPLICA_WAIT_SLEEP_SECONDS
sleep_attempts(worker_class).times do
sleep sleep_duration(worker_class)
break if databases_in_sync?(wal_locations)
end
@ -70,6 +74,14 @@ module Gitlab
end
end
def sleep_duration(worker_class)
worker_class.get_urgency == :high ? URGENT_REPLICA_WAIT_SLEEP_SECONDS : REPLICA_WAIT_SLEEP_SECONDS
end
def sleep_attempts(worker_class)
worker_class.get_urgency == :high ? URGENT_SLEEP_ATTEMPTS : SLEEP_ATTEMPTS
end
def get_wal_locations(job)
job['dedup_wal_locations'] || job['wal_locations']
end

View File

@ -13478,7 +13478,7 @@ msgstr ""
msgid "CodeSuggestions|Manage seat assignments for %{addOnName} or run a health check to identify problems."
msgstr ""
msgid "CodeSuggestions|Manage seat assignments for %{addOnName}."
msgid "CodeSuggestions|Manage seat assignments for %{addOnName} within your group."
msgstr ""
msgid "CodeSuggestions|No health problems detected"
@ -23633,6 +23633,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
msgid "Following"
msgstr ""
msgid "Following tags don't exist"
msgstr ""
@ -34029,6 +34032,9 @@ msgstr ""
msgid "MergeChecks|View locked files"
msgstr ""
msgid "MergeChecks|View policies"
msgstr ""
msgid "MergeConflict|Commit to source branch"
msgstr ""
@ -38096,9 +38102,6 @@ msgstr ""
msgid "Open Selection"
msgstr ""
msgid "Open action menu"
msgstr ""
msgid "Open color picker"
msgstr ""
@ -54035,12 +54038,6 @@ msgstr ""
msgid "Switch branch/tag"
msgstr ""
msgid "Switch to new dashboard"
msgstr ""
msgid "Switch to old dashboard"
msgstr ""
msgid "Switch to plain text editing"
msgstr ""
@ -59035,6 +59032,9 @@ msgstr ""
msgid "UsageQuota|Registry"
msgstr ""
msgid "UsageQuota|Seat utilization"
msgstr ""
msgid "UsageQuota|Shared bits of code and text."
msgstr ""

View File

@ -5,12 +5,12 @@ module QA
module Layout
class Banner < Page::Base
view 'app/views/layouts/header/_read_only_banner.html.haml' do
element :flash_notice, ".flash-notice" # rubocop:disable QA/ElementWithPattern
element 'read-only-banner'
end
def has_notice?(message)
page.within('.flash-notice') do
!!find('span', text: message)
page.within('.gl-alert') do
!!find('.gl-alert-body', text: message)
end
end
end

View File

@ -30,6 +30,7 @@ RSpec.describe 'Database schema',
ci_sources_pipelines: [%w[source_partition_id source_pipeline_id], %w[partition_id pipeline_id]],
ci_sources_projects: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
ci_stages: [%w[partition_id pipeline_id]], # the index on pipeline_id is sufficient
issues: [%w[correct_work_item_type_id]],
notes: %w[namespace_id], # this index is added in an async manner, hence it needs to be ignored in the first phase.
p_ci_build_trace_metadata: [%w[partition_id build_id], %w[partition_id trace_artifact_id]], # the index on build_id is enough
p_ci_builds: [%w[partition_id stage_id], %w[partition_id execution_config_id], %w[auto_canceled_by_partition_id auto_canceled_by_id], %w[upstream_pipeline_partition_id upstream_pipeline_id], %w[partition_id commit_id]], # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142804#note_1745483081
@ -129,7 +130,7 @@ RSpec.describe 'Database schema',
gitlab_subscription_histories: %w[gitlab_subscription_id hosted_plan_id namespace_id],
identities: %w[user_id],
import_failures: %w[project_id],
issues: %w[last_edited_by_id state_id correct_work_item_type_id], # correct_work_item_type_id is a temp column
issues: %w[last_edited_by_id state_id],
issue_emails: %w[email_message_id],
jira_tracker_data: %w[jira_issue_transition_id],
keys: %w[user_id],

View File

@ -89,7 +89,7 @@ RSpec.describe 'Profile > Applications', feature_category: :user_profile do
end
end
it 'deletes an authorized application', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/444512' do
it 'deletes an authorized application' do
token
visit oauth_applications_path
@ -109,8 +109,7 @@ RSpec.describe 'Profile > Applications', feature_category: :user_profile do
end
end
it 'deletes an anonymous authorized application',
quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/444513' do
it 'deletes an anonymous authorized application' do
anonymous_token
visit oauth_applications_path

View File

@ -1,33 +0,0 @@
import { shallowMount } from '@vue/test-utils';
import { GlDisclosureDropdown } from '@gitlab/ui';
import { useMockInternalEventsTracking } from 'helpers/tracking_internal_events_helper';
import ActionDropdown from '~/merge_request_dashboard/components/action_dropdown.vue';
describe('Merge request dashboard action dropdown', () => {
let wrapper;
const { bindInternalEventDocument } = useMockInternalEventsTracking();
function createComponent(experimentEnabled) {
wrapper = shallowMount(ActionDropdown, {
provide: { experimentEnabled },
});
}
it.each`
experimentEnabled | value
${true} | ${1}
${false} | ${0}
`('calls tracking method with value $value', async ({ experimentEnabled, value }) => {
createComponent(experimentEnabled);
const { trackEventSpy } = bindInternalEventDocument(wrapper.element);
await wrapper.findComponent(GlDisclosureDropdown).vm.$emit('action', { id: 0 });
expect(trackEventSpy).toHaveBeenCalledWith(
'toggle_merge_request_redesign',
{ value },
undefined,
);
});
});

View File

@ -1,4 +1,5 @@
import Vue from 'vue';
import VueRouter from 'vue-router';
import VueApollo from 'vue-apollo';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
@ -12,6 +13,7 @@ import assigneeCountQuery from '~/merge_request_dashboard/queries/assignee_count
import { createMockMergeRequest } from '../mock_data';
Vue.use(VueApollo);
Vue.use(VueRouter);
describe('Merge requests app component', () => {
let wrapper;
@ -63,16 +65,25 @@ describe('Merge requests app component', () => {
wrapper = shallowMountExtended(App, {
apolloProvider,
router: new VueRouter({}),
propsData: {
lists: [
tabs: [
{
title: 'Assigned merge requests',
query: 'assignedMergeRequests',
variables: { state: 'opened' },
title: 'Needs attention',
lists: [
{
title: 'Assigned merge requests',
query: 'assignedMergeRequests',
variables: { state: 'opened' },
},
],
},
],
...props,
},
provide: {
mergeRequestsSearchDashboardPath: '/search',
},
stubs: {
MergeRequestsQuery,
CollapsibleSection,

View File

@ -399,29 +399,6 @@ RSpec.describe MergeRequestsHelper, feature_category: :code_review_workflow do
end
end
describe '#merge_request_dashboard_enabled?' do
using RSpec::Parameterized::TableSyntax
let_it_be(:current_user) { build_stubbed(:user) }
where(:enabled, :search_page, :result) do
true | true | false
true | false | true
false | false | false
false | true | false
end
with_them do
before do
stub_feature_flags(merge_request_dashboard: enabled)
allow(helper).to receive(:current_page?).and_return(search_page)
end
it { expect(helper.merge_request_dashboard_enabled?(current_user)).to eq(result) }
end
end
describe '#diffs_stream_url' do
let_it_be(:offset) { 5 }
let(:merge_request) { create(:merge_request_with_diffs) }

View File

@ -173,7 +173,6 @@ RSpec.describe Gitlab::Database::LoadBalancing::Host, feature_category: :databas
end
it 'logs the online event' do
allow(Gitlab::Database::LoadBalancing::Logger).to receive(:info).and_call_original
expect(Gitlab::Database::LoadBalancing::Logger)
.to receive(:info)
.with(hash_including(event: :host_online))
@ -449,7 +448,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::Host, feature_category: :databas
context 'with the flag set' do
before do
stub_feature_flags(load_balancer_low_statement_timeout: Feature.current_pod)
stub_feature_flags(load_balancer_low_statement_timeout: true)
end
it 'returns quickly if the underlying query takes a long time' do

View File

@ -18,7 +18,8 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware, :clean_
replication_lag!(false)
Gitlab::Database::LoadBalancing::SessionMap.clear_session
stub_const("#{described_class.name}::REPLICA_WAIT_SLEEP_SECONDS", 0.0)
stub_const("#{described_class.name}::REPLICA_WAIT_SLEEP_SECONDS", 0.02)
stub_const("#{described_class.name}::URGENT_REPLICA_WAIT_SLEEP_SECONDS", 0.01)
end
after do
@ -26,7 +27,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware, :clean_
end
describe '#call' do
shared_context 'data consistency worker class' do |data_consistency, feature_flag|
shared_context 'data consistency worker class' do |data_consistency, feature_flag, urgency = :low|
let(:worker_class) do
Class.new do
def self.name
@ -36,6 +37,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware, :clean_
include ApplicationWorker
data_consistency data_consistency, feature_flag: feature_flag
urgency urgency
def perform(*args); end
end
@ -119,9 +121,16 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware, :clean_
end
end
shared_examples_for 'essential sleep' do
shared_examples_for 'essential sleep' do |sleep_duration = 0.02, sleep_attempts = described_class::SLEEP_ATTEMPTS|
context 'when WAL locations are blank', :freeze_time do
let(:job) { { "retry" => 3, "job_id" => "a180b47c-3fd6-41b8-81e9-34da61c3400e", "wal_locations" => {}, "created_at" => Time.current.to_f - (described_class::REPLICA_WAIT_SLEEP_SECONDS + 0.2) } }
let(:job) do
{
"retry" => 3,
"job_id" => "a180b47c-3fd6-41b8-81e9-34da61c3400e",
"wal_locations" => {},
"created_at" => Time.current.to_f - (sleep_duration + 0.2)
}
end
it 'does not sleep' do
expect(middleware).not_to receive(:sleep)
@ -134,7 +143,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware, :clean_
let(:job) { { "retry" => 3, "job_id" => "a180b47c-3fd6-41b8-81e9-34da61c3400e", 'wal_locations' => wal_locations, "created_at" => Time.current.to_f - elapsed_time } }
context 'when delay interval has not elapsed' do
let(:elapsed_time) { described_class::REPLICA_WAIT_SLEEP_SECONDS + 0.2 }
let(:elapsed_time) { sleep_duration + 0.2 }
context 'when replica is up to date' do
before do
@ -158,7 +167,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware, :clean_
end
it 'sleeps until the minimum delay is reached' do
expect(middleware).to receive(:sleep).with(described_class::REPLICA_WAIT_SLEEP_SECONDS)
expect(middleware).to receive(:sleep).with(sleep_duration)
run_middleware
end
@ -172,7 +181,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware, :clean_
end
it 'sleeps until the maximum delay is reached' do
expect(middleware).to receive(:sleep).exactly(3).times.with(described_class::REPLICA_WAIT_SLEEP_SECONDS)
expect(middleware).to receive(:sleep).exactly(sleep_attempts).times.with(sleep_duration)
run_middleware
end
@ -280,6 +289,40 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware, :clean_
include_examples 'stick to the primary', 'primary'
end
end
context 'when worker data consistency is :sticky and urgent' do
include_context 'data consistency worker class', :sticky, :load_balancing_for_test_data_consistency_worker, :high
include_examples 'sticks based on data consistency'
include_examples 'essential sleep', 0.01, described_class::URGENT_SLEEP_ATTEMPTS
context 'when replica is not up to date' do
before do
Gitlab::Database::LoadBalancing.each_load_balancer do |lb|
allow(lb).to receive(:select_up_to_date_host).and_return(none_caught_up)
end
end
include_examples 'stick to the primary', 'primary'
end
end
context 'when worker data consistency is :delayed and urgent' do
include_context 'data consistency worker class', :delayed, :load_balancing_for_test_data_consistency_worker, :high
include_examples 'sticks based on data consistency'
include_examples 'essential sleep', 0.01, described_class::URGENT_SLEEP_ATTEMPTS
context 'when replica is not up to date' do
before do
Gitlab::Database::LoadBalancing.each_load_balancer do |lb|
allow(lb).to receive(:select_up_to_date_host).and_return(none_caught_up)
end
end
include_examples 'stick to the primary', 'primary'
end
end
end
describe '#databases_in_sync?' do

View File

@ -176,14 +176,15 @@ RSpec.describe 'new tables missing sharding_key', feature_category: :cell do
FROM information_schema.columns c
LEFT JOIN postgres_foreign_keys fk
ON fk.constrained_table_name = c.table_name AND fk.constrained_columns = '{organization_id}' and fk.referenced_columns = '{id}'
WHERE c.column_name = 'organization_id' AND (c.column_default IS NOT NULL OR c.is_nullable::boolean OR fk.name IS NULL)
WHERE c.column_name = 'organization_id'
AND (fk.referenced_table_name = 'organizations' OR fk.referenced_table_name IS NULL)
AND (c.column_default IS NOT NULL OR c.is_nullable::boolean OR fk.name IS NULL)
ORDER BY c.table_name;
SQL
# To add a table to this list, create an issue under https://gitlab.com/groups/gitlab-org/-/epics/11670.
# Use https://gitlab.com/gitlab-org/gitlab/-/issues/476206 as an example.
work_in_progress = {
"customer_relations_contacts" => 'https://gitlab.com/gitlab-org/gitlab/-/issues/476206',
"dependency_list_export_parts" => 'https://gitlab.com/gitlab-org/gitlab/-/issues/476207',
"dependency_list_exports" => 'https://gitlab.com/gitlab-org/gitlab/-/issues/476208',
"namespaces" => 'https://gitlab.com/gitlab-org/gitlab/-/issues/476209',

View File

@ -21,9 +21,10 @@ RSpec.describe Gitlab::ImportExport::Json::StreamingSerializer, :clean_gitlab_re
end
let_it_be(:issue) do
# TODO: .reload can be removed after the migration https://gitlab.com/gitlab-org/gitlab/-/issues/497857
create(:issue,
assignees: [user],
project: exportable)
project: exportable).reload
end
let(:exportable_path) { 'project' }
@ -321,7 +322,8 @@ RSpec.describe Gitlab::ImportExport::Json::StreamingSerializer, :clean_gitlab_re
describe 'with inaccessible associations' do
let_it_be(:milestone) { create(:milestone, project: exportable) }
let_it_be(:issue) { create(:issue, assignees: [user], project: exportable, milestone: milestone) }
# TODO: .reload can be removed after the migration https://gitlab.com/gitlab-org/gitlab/-/issues/497857
let_it_be(:issue) { create(:issue, assignees: [user], project: exportable, milestone: milestone).reload }
let_it_be(:label1) { create(:label, project: exportable) }
let_it_be(:label2) { create(:label, project: exportable) }
let_it_be(:link1) { create(:label_link, label: label1, target: issue) }

View File

@ -31,6 +31,20 @@ RSpec.describe ResourceAccessTokens::CreateService, feature_category: :system_ac
end
end
shared_examples 'deletes failed project bot' do
it 'calls DeleteUserWorker for the project bot' do
expect_next_instance_of(User) do |project_bot|
project_bot.id = User.maximum(:id) + 1
expect(DeleteUserWorker).to receive(:perform_async).with(
user.id, project_bot.id,
hard_delete: true, skip_authorization: true, reason_for_deletion: "Access token creation failed"
).and_call_original
end
subject
end
end
shared_examples 'correct error message' do
it 'returns correct error message' do
expect(subject.error?).to be true
@ -279,18 +293,16 @@ RSpec.describe ResourceAccessTokens::CreateService, feature_category: :system_ac
it_behaves_like 'token creation fails'
it_behaves_like 'correct error message'
it_behaves_like 'deletes failed project bot'
end
end
context "when access provisioning fails" do
let_it_be(:bot_user) { create(:user, :project_bot) }
let(:unpersisted_member) { build(:project_member, source: resource, user: bot_user) }
let(:unpersisted_member) { build(:project_member, source: resource) }
let(:error_message) { 'Could not provision maintainer access to the access token. ERROR: error message' }
before do
allow_next_instance_of(ResourceAccessTokens::CreateService) do |service|
allow(service).to receive(:create_user).and_return(bot_user)
allow(service).to receive(:create_membership).and_return(unpersisted_member)
end
@ -303,6 +315,7 @@ RSpec.describe ResourceAccessTokens::CreateService, feature_category: :system_ac
it_behaves_like 'token creation fails'
it_behaves_like 'correct error message'
it_behaves_like 'deletes failed project bot'
end
context 'with MAINTAINER access_level, in string format' do
@ -310,6 +323,7 @@ RSpec.describe ResourceAccessTokens::CreateService, feature_category: :system_ac
it_behaves_like 'token creation fails'
it_behaves_like 'correct error message'
it_behaves_like 'deletes failed project bot'
end
end
end

View File

@ -45,7 +45,10 @@ RSpec.describe ResourceAccessTokens::RevokeService, feature_category: :system_ac
it { expect(subject.message).to eq("Access token #{access_token.name} has been revoked and the bot user has been scheduled for deletion.") }
it 'calls delete user worker' do
expect(DeleteUserWorker).to receive(:perform_async).with(user.id, resource_bot.id, skip_authorization: true)
expect(DeleteUserWorker).to receive(:perform_async).with(
user.id, resource_bot.id,
skip_authorization: true, reason_for_deletion: "Access token revoked"
)
subject
end

View File

@ -21,7 +21,8 @@ RSpec.shared_examples 'permission level for issue mutation is correctly verified
)
end
let(:expected) { issue_attributes(issue) }
# TODO: .reload can be removed after the migration https://gitlab.com/gitlab-org/gitlab/-/issues/497857
let(:expected) { issue_attributes(issue.reload) }
shared_examples_for 'when the user does not have access to the resource' do |raise_for_assigned_and_author|
before do

View File

@ -197,7 +197,11 @@ RSpec.shared_examples 'PUT resource access tokens available' do
end
it 'calls delete user worker' do
expect(DeleteUserWorker).to receive(:perform_async).with(user.id, access_token_user.id, skip_authorization: true)
expect(DeleteUserWorker).to receive(:perform_async).with(
user.id,
access_token_user.id,
skip_authorization: true, reason_for_deletion: "Access token revoked"
)
subject
end

View File

@ -11,6 +11,8 @@ import {
IS_JH,
SOURCEGRAPH_PUBLIC_PATH,
GITLAB_WEB_IDE_PUBLIC_PATH,
PDF_JS_WORKER_PUBLIC_PATH,
PDF_JS_CMAPS_PUBLIC_PATH,
copyFilesPatterns,
} from './config/webpack.constants';
/* eslint-disable import/extensions */
@ -118,6 +120,8 @@ export default defineConfig({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'process.env.SOURCEGRAPH_PUBLIC_PATH': JSON.stringify(SOURCEGRAPH_PUBLIC_PATH),
'process.env.GITLAB_WEB_IDE_PUBLIC_PATH': JSON.stringify(GITLAB_WEB_IDE_PUBLIC_PATH),
'process.env.PDF_JS_WORKER_PUBLIC_PATH': JSON.stringify(PDF_JS_WORKER_PUBLIC_PATH),
'process.env.PDF_JS_CMAPS_PUBLIC_PATH': JSON.stringify(PDF_JS_CMAPS_PUBLIC_PATH),
'window.IS_VITE': JSON.stringify(true),
'window.VUE_DEVTOOLS_CONFIG.openInEditorHost': JSON.stringify(
viteGDKConfig.hmr