Add latest changes from gitlab-org/gitlab@master
2
Gemfile
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"},
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"},
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,7 +124,6 @@ $profile-grid-flex: 1fr;
|
|||
}
|
||||
|
||||
.ssh-keys-list {
|
||||
.last-used-at,
|
||||
.expires,
|
||||
.key-created-at {
|
||||
line-height: 32px;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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|
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 }),
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
150184cdc3da0f4f7919ce101f577542b3acbd65958b1ed4826364183247e40e
|
||||
|
|
@ -0,0 +1 @@
|
|||
8f38307f2ffee7553c46c188dbf18af57a28d306811b75e106bfc83b15df5f05
|
||||
|
|
@ -0,0 +1 @@
|
|||
eff86eb6c3d6002da8128edf071104bd2be7a88f2c89d477b24d1b8387aa9ee5
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 15 KiB |
|
|
@ -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.
|
||||
|
||||

|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||

|
||||

|
||||
|
||||
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
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
|
@ -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 -->
|
||||
|
|
@ -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 |
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
|
@ -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:
|
||||
|
||||

|
||||

|
||||
|
||||
#### 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:
|
||||
|
||||

|
||||

|
||||
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||

|
||||

|
||||
|
||||
You can also [view and remove existing locks](#view-and-remove-existing-locks) from the GitLab UI.
|
||||
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
|
@ -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}**):
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
||||

|
||||

|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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],
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||