Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
f2e8d3f288
commit
7a4582e967
|
|
@ -57,7 +57,7 @@ release-environments-qa:
|
|||
- .qa-base
|
||||
timeout: 3h
|
||||
variables:
|
||||
QA_SCENARIO: "Test::Instance::Smoke"
|
||||
QA_SCENARIO: "Test::Instance::Any"
|
||||
RELEASE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_COMMIT_SHA}"
|
||||
GITLAB_QA_OPTS: --address "https://gitlab.${ENVIRONMENT}.release.gke.gitlab.net"
|
||||
GITLAB_INITIAL_ROOT_PASSWORD: "${RELEASE_ENVIRONMENTS_ROOT_PASSWORD}"
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
f68eed8a9d41bfe20840e87c8a9f6cd58db30de3
|
||||
cb8bec7d843749b73a0fc06f44e6aec25b378d92
|
||||
|
|
|
|||
6
Gemfile
6
Gemfile
|
|
@ -376,7 +376,7 @@ gem 'thrift', '>= 0.16.0' # rubocop:todo Gemfile/MissingFeatureCategory
|
|||
|
||||
# I18n
|
||||
gem 'rails-i18n', '~> 7.0', feature_category: :internationalization
|
||||
gem 'gettext_i18n_rails', '~> 1.11.0', feature_category: :internationalization
|
||||
gem 'gettext_i18n_rails', '~> 1.12.0', feature_category: :internationalization
|
||||
gem 'gettext', '~> 3.4', '>= 3.4.9',
|
||||
require: false,
|
||||
group: [:development, :test],
|
||||
|
|
@ -419,6 +419,8 @@ group :development do
|
|||
gem 'ruby-lsp-rails', "~> 0.3.0", feature_category: :tooling
|
||||
|
||||
gem 'ruby-lsp-rspec', "~> 0.1.10", require: false, feature_category: :tooling
|
||||
|
||||
gem 'gdk-toogle', '~> 0.9', require: 'toogle', feature_category: :tooling
|
||||
end
|
||||
|
||||
group :development, :test do
|
||||
|
|
@ -500,7 +502,7 @@ end
|
|||
group :test do
|
||||
gem 'fuubar', '~> 2.2.0' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
gem 'rspec-retry', '~> 0.6.2', feature_category: :tooling
|
||||
gem 'rspec_profiling', '~> 0.0.8', feature_category: :tooling
|
||||
gem 'rspec_profiling', '~> 0.0.9', feature_category: :tooling
|
||||
gem 'rspec-benchmark', '~> 0.6.0', feature_category: :tooling
|
||||
gem 'rspec-parameterized', '~> 1.0', require: false, feature_category: :tooling
|
||||
gem 'os', '~> 1.1', '>= 1.1.4', feature_category: :tooling
|
||||
|
|
|
|||
|
|
@ -201,10 +201,11 @@
|
|||
{"name":"fuubar","version":"2.2.0","platform":"ruby","checksum":"9b0263c4074f39c68b37f1e4e69a7d3cfc7523c41bea43601235daa723179b4a"},
|
||||
{"name":"fuzzyurl","version":"0.9.0","platform":"ruby","checksum":"542efa80f2bcaadbdc402c2f0b572f2e335a1d53e375aecad68bbb3d86860c0f"},
|
||||
{"name":"gapic-common","version":"0.20.0","platform":"ruby","checksum":"af304704b440f7a2a1e8ce6ecce109a67b79fa173f36f11b513b8a35ce509366"},
|
||||
{"name":"gdk-toogle","version":"0.9.3","platform":"ruby","checksum":"897f5d3d5d2b2d4a4438c5fb8227f2888d3ad0180353c0d62d0dece2cd22eebf"},
|
||||
{"name":"gemoji","version":"3.0.1","platform":"ruby","checksum":"80553f2f4932a7a95fb1b3c7c63f7dd937e7c8c610164bbdea28fd06eba5f36d"},
|
||||
{"name":"get_process_mem","version":"0.2.7","platform":"ruby","checksum":"4afd3c3641dd6a817c09806c7d6d509d8a9984512ac38dea8b917426bbf77eba"},
|
||||
{"name":"gettext","version":"3.4.9","platform":"ruby","checksum":"292864fe6a15c224cee4125a4a72fab426fdbb280e4cff3cfe44935f549b009a"},
|
||||
{"name":"gettext_i18n_rails","version":"1.11.0","platform":"ruby","checksum":"e19c7e4a256c500f7f38396dca44a282b9838ae278f57c362993a54964b22bbe"},
|
||||
{"name":"gettext_i18n_rails","version":"1.12.0","platform":"ruby","checksum":"6ac4817731a9e2ce47e1e83381ac34f9142263bc2911aaaafb2526d2f1afc1be"},
|
||||
{"name":"git","version":"1.18.0","platform":"ruby","checksum":"c9b80462e4565cd3d7a9ba8440c41d2c52244b17b0dad0bfddb46de70630c465"},
|
||||
{"name":"gitaly","version":"16.10.0.pre.rc1","platform":"ruby","checksum":"483162f2c271fc78e985ad6b25bd06f69a91a4fabc2ff6f25909db7f69458cdb"},
|
||||
{"name":"gitlab","version":"4.19.0","platform":"ruby","checksum":"3f645e3e195dbc24f0834fbf83e8ccfb2056d8e9712b01a640aad418a6949679"},
|
||||
|
|
@ -560,7 +561,7 @@
|
|||
{"name":"rspec-retry","version":"0.6.2","platform":"ruby","checksum":"6101ba23a38809811ae3484acde4ab481c54d846ac66d5037ccb40131a60d858"},
|
||||
{"name":"rspec-support","version":"3.12.0","platform":"ruby","checksum":"dd4d44b247ff679b95b5607ac5641d197a5f9b1d33f916123cb98fc5f917c58b"},
|
||||
{"name":"rspec_junit_formatter","version":"0.6.0","platform":"ruby","checksum":"40dde674e6ae4e6cc0ff560da25497677e34fefd2338cc467a8972f602b62b15"},
|
||||
{"name":"rspec_profiling","version":"0.0.8","platform":"ruby","checksum":"b31ee456811b8914806a9d46b07959877fc319a0515b1c207987d70a1c499445"},
|
||||
{"name":"rspec_profiling","version":"0.0.9","platform":"ruby","checksum":"6199be2daeaa14bac3d10d704dbb0a8df052cf046332c505603263aea24f7590"},
|
||||
{"name":"rubocop","version":"1.57.2","platform":"ruby","checksum":"8f679dfe42d7821dc61dafb17d14b1294343157a197b9f8a23720ca17fb9161b"},
|
||||
{"name":"rubocop-ast","version":"1.29.0","platform":"ruby","checksum":"d1da2ab279a074baefc81758ac430c5768a8da8c7438dd4e5819ce5984d00ba1"},
|
||||
{"name":"rubocop-capybara","version":"2.19.0","platform":"ruby","checksum":"fa329e0f185be313fa5dabd6056f83a718db7f4a259aa97fc287a40254899ccb"},
|
||||
|
|
|
|||
12
Gemfile.lock
12
Gemfile.lock
|
|
@ -669,6 +669,9 @@ GEM
|
|||
googleapis-common-protos-types (>= 1.3.1, < 2.a)
|
||||
googleauth (~> 1.0)
|
||||
grpc (~> 1.36)
|
||||
gdk-toogle (0.9.3)
|
||||
haml
|
||||
rails (>= 7.0.4.2)
|
||||
gemoji (3.0.1)
|
||||
get_process_mem (0.2.7)
|
||||
ffi (~> 1.0)
|
||||
|
|
@ -678,7 +681,7 @@ GEM
|
|||
prime
|
||||
racc
|
||||
text (>= 1.3.0)
|
||||
gettext_i18n_rails (1.11.0)
|
||||
gettext_i18n_rails (1.12.0)
|
||||
fast_gettext (>= 0.9.0)
|
||||
git (1.18.0)
|
||||
addressable (~> 2.8)
|
||||
|
|
@ -1471,7 +1474,7 @@ GEM
|
|||
rspec-support (3.12.0)
|
||||
rspec_junit_formatter (0.6.0)
|
||||
rspec-core (>= 2, < 4, != 2.12.0)
|
||||
rspec_profiling (0.0.8)
|
||||
rspec_profiling (0.0.9)
|
||||
activerecord
|
||||
get_process_mem
|
||||
rails
|
||||
|
|
@ -1899,8 +1902,9 @@ DEPENDENCIES
|
|||
fog-local (~> 0.8)
|
||||
fugit (~> 1.8.1)
|
||||
fuubar (~> 2.2.0)
|
||||
gdk-toogle (~> 0.9)
|
||||
gettext (~> 3.4, >= 3.4.9)
|
||||
gettext_i18n_rails (~> 1.11.0)
|
||||
gettext_i18n_rails (~> 1.12.0)
|
||||
gitaly (~> 16.10.0.pre.rc1)
|
||||
gitlab-backup-cli!
|
||||
gitlab-chronic (~> 0.10.5)
|
||||
|
|
@ -2076,7 +2080,7 @@ DEPENDENCIES
|
|||
rspec-rails (~> 6.1.1)
|
||||
rspec-retry (~> 0.6.2)
|
||||
rspec_junit_formatter
|
||||
rspec_profiling (~> 0.0.8)
|
||||
rspec_profiling (~> 0.0.9)
|
||||
rubocop
|
||||
ruby-fogbugz (~> 0.3.0)
|
||||
ruby-lsp (~> 0.14.4)
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export default {
|
|||
tokens() {
|
||||
return [
|
||||
{
|
||||
icon: 'clock',
|
||||
icon: 'milestone',
|
||||
title: TOKEN_TITLE_MILESTONE,
|
||||
type: TOKEN_TYPE_MILESTONE,
|
||||
token: MilestoneToken,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export const initReviewBar = () => {
|
|||
ReviewBar: () => import('./components/review_bar.vue'),
|
||||
},
|
||||
provide: {
|
||||
newCommentTemplatePath: el.dataset.newCommentTemplatePath,
|
||||
newCommentTemplatePaths: JSON.parse(el.dataset.newCommentTemplatePaths),
|
||||
canSummarize: parseBoolean(el.dataset.canSummarize),
|
||||
},
|
||||
computed: {
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ export default {
|
|||
{
|
||||
type: TOKEN_TYPE_MILESTONE,
|
||||
title: TOKEN_TITLE_MILESTONE,
|
||||
icon: 'clock',
|
||||
icon: 'milestone',
|
||||
symbol: '%',
|
||||
token: MilestoneToken,
|
||||
unique: true,
|
||||
|
|
|
|||
|
|
@ -1,20 +1,40 @@
|
|||
<script>
|
||||
import { GlAvatar, GlAvatarLink, GlBadge } from '@gitlab/ui';
|
||||
import {
|
||||
GlAvatar,
|
||||
GlAvatarLink,
|
||||
GlBadge,
|
||||
GlDisclosureDropdown,
|
||||
GlDisclosureDropdownItem,
|
||||
GlTooltipDirective,
|
||||
} from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { cleanLeadingSeparator } from '~/lib/utils/url_utility';
|
||||
import CiIcon from '~/vue_shared/components/ci_icon/ci_icon.vue';
|
||||
import AbuseCategorySelector from '~/abuse_reports/components/abuse_category_selector.vue';
|
||||
import CiResourceAbout from './ci_resource_about.vue';
|
||||
import CiResourceHeaderSkeletonLoader from './ci_resource_header_skeleton_loader.vue';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
moreActionsLabel: __('More actions'),
|
||||
reportAbuse: __('Report abuse to administrator'),
|
||||
},
|
||||
components: {
|
||||
AbuseCategorySelector,
|
||||
CiIcon,
|
||||
CiResourceAbout,
|
||||
CiResourceHeaderSkeletonLoader,
|
||||
GlAvatar,
|
||||
GlAvatarLink,
|
||||
GlDisclosureDropdown,
|
||||
GlDisclosureDropdownItem,
|
||||
GlBadge,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
inject: ['reportAbusePath'],
|
||||
props: {
|
||||
isLoadingDetails: {
|
||||
type: Boolean,
|
||||
|
|
@ -44,7 +64,17 @@ export default {
|
|||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isReportAbuseDrawerOpen: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
authorId() {
|
||||
return this.hasLatestVersion && this.latestVersion?.author?.state === 'active'
|
||||
? getIdFromGraphQLId(this.latestVersion?.author?.id)
|
||||
: 0;
|
||||
},
|
||||
entityId() {
|
||||
return getIdFromGraphQLId(this.resource.id);
|
||||
},
|
||||
|
|
@ -57,6 +87,9 @@ export default {
|
|||
latestVersion() {
|
||||
return this.resource?.versions?.nodes[0] || {};
|
||||
},
|
||||
reportedFromUrl() {
|
||||
return window.location.href;
|
||||
},
|
||||
versionBadgeText() {
|
||||
return this.latestVersion.name;
|
||||
},
|
||||
|
|
@ -64,45 +97,77 @@ export default {
|
|||
return cleanLeadingSeparator(this.resource?.webPath);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onAbuseButtonClicked() {
|
||||
this.toggleReportAbuseDrawer(true);
|
||||
},
|
||||
toggleReportAbuseDrawer(isOpen) {
|
||||
this.isReportAbuseDrawerOpen = isOpen;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<ci-resource-header-skeleton-loader v-if="isLoadingSharedData" class="gl-py-5" />
|
||||
<div v-else class="gl-display-flex gl-py-5">
|
||||
<gl-avatar-link :href="resource.webPath">
|
||||
<gl-avatar
|
||||
class="gl-mr-4"
|
||||
:entity-id="entityId"
|
||||
:entity-name="resource.name"
|
||||
shape="rect"
|
||||
:size="64"
|
||||
:src="resource.icon"
|
||||
/>
|
||||
</gl-avatar-link>
|
||||
<div
|
||||
class="gl-display-flex gl-flex-direction-column gl-align-items-flex-start gl-justify-content-center"
|
||||
>
|
||||
<div class="gl-font-sm gl-text-secondary">
|
||||
{{ webPath }}
|
||||
<div v-else class="gl-display-flex gl-justify-content-space-between gl-py-5">
|
||||
<div class="gl-display-flex">
|
||||
<gl-avatar-link :href="resource.webPath">
|
||||
<gl-avatar
|
||||
class="gl-mr-4"
|
||||
:entity-id="entityId"
|
||||
:entity-name="resource.name"
|
||||
shape="rect"
|
||||
:size="64"
|
||||
:src="resource.icon"
|
||||
/>
|
||||
</gl-avatar-link>
|
||||
<div
|
||||
class="gl-display-flex gl-flex-direction-column gl-align-items-flex-start gl-justify-content-center"
|
||||
>
|
||||
<div class="gl-font-sm gl-text-secondary">
|
||||
{{ webPath }}
|
||||
</div>
|
||||
<span class="gl-display-flex">
|
||||
<div class="gl-font-lg gl-font-weight-bold">{{ resource.name }}</div>
|
||||
<gl-badge
|
||||
v-if="hasLatestVersion"
|
||||
size="sm"
|
||||
class="gl-ml-3 gl-my-1"
|
||||
:href="latestVersion.path"
|
||||
>
|
||||
{{ versionBadgeText }}
|
||||
</gl-badge>
|
||||
</span>
|
||||
<ci-icon
|
||||
v-if="hasPipelineStatus"
|
||||
:status="pipelineStatus"
|
||||
show-status-text
|
||||
class="gl-mt-2"
|
||||
/>
|
||||
</div>
|
||||
<span class="gl-display-flex">
|
||||
<div class="gl-font-lg gl-font-weight-bold">{{ resource.name }}</div>
|
||||
<gl-badge
|
||||
v-if="hasLatestVersion"
|
||||
size="sm"
|
||||
class="gl-ml-3 gl-my-1"
|
||||
:href="latestVersion.path"
|
||||
</div>
|
||||
<div>
|
||||
<gl-disclosure-dropdown
|
||||
v-gl-tooltip
|
||||
:title="$options.i18n.moreActionsLabel"
|
||||
:toggle-text="$options.i18n.moreActionsLabel"
|
||||
text-sr-only
|
||||
icon="ellipsis_v"
|
||||
category="tertiary"
|
||||
placement="right"
|
||||
class="note-action-button more-actions-toggle"
|
||||
no-caret
|
||||
>
|
||||
<gl-disclosure-dropdown-item
|
||||
data-testid="report-abuse-button"
|
||||
@action="onAbuseButtonClicked"
|
||||
>
|
||||
{{ versionBadgeText }}
|
||||
</gl-badge>
|
||||
</span>
|
||||
<ci-icon
|
||||
v-if="hasPipelineStatus"
|
||||
:status="pipelineStatus"
|
||||
show-status-text
|
||||
class="gl-mt-2"
|
||||
/>
|
||||
<template #list-item>
|
||||
{{ $options.i18n.reportAbuse }}
|
||||
</template>
|
||||
</gl-disclosure-dropdown-item>
|
||||
</gl-disclosure-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<ci-resource-about
|
||||
|
|
@ -120,5 +185,12 @@ export default {
|
|||
<p v-else class="gl-mt-3">
|
||||
{{ resource.description }}
|
||||
</p>
|
||||
<abuse-category-selector
|
||||
v-if="hasLatestVersion && isReportAbuseDrawerOpen && reportAbusePath"
|
||||
:reported-user-id="authorId"
|
||||
:reported-from-url="reportedFromUrl"
|
||||
:show-drawer="isReportAbuseDrawerOpen"
|
||||
@close-drawer="toggleReportAbuseDrawer(false)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ fragment CatalogResourceFields on CiCatalogResource {
|
|||
author {
|
||||
id
|
||||
name
|
||||
state
|
||||
webUrl
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export const initCatalog = (selector = '#js-ci-cd-catalog') => {
|
|||
}
|
||||
|
||||
const { dataset } = el;
|
||||
const { ciCatalogPath } = dataset;
|
||||
const { ciCatalogPath, reportAbusePath } = dataset;
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
|
|
@ -30,6 +30,7 @@ export const initCatalog = (selector = '#js-ci-cd-catalog') => {
|
|||
apolloProvider,
|
||||
provide: {
|
||||
ciCatalogPath,
|
||||
reportAbusePath,
|
||||
},
|
||||
render(h) {
|
||||
return h(GlobalCatalog);
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export default {
|
|||
HeaderDivider,
|
||||
},
|
||||
inject: {
|
||||
newCommentTemplatePath: { default: null },
|
||||
newCommentTemplatePaths: { default: () => [] },
|
||||
tiptapEditor: { default: null },
|
||||
contentEditor: { default: null },
|
||||
},
|
||||
|
|
@ -199,11 +199,11 @@ export default {
|
|||
:label="__('Add a quick action')"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<header-divider v-if="newCommentTemplatePath" />
|
||||
<header-divider v-if="newCommentTemplatePaths.length" />
|
||||
</div>
|
||||
<comment-templates-dropdown
|
||||
v-if="newCommentTemplatePath"
|
||||
:new-comment-template-path="newCommentTemplatePath"
|
||||
v-if="newCommentTemplatePaths.length"
|
||||
:new-comment-template-paths="newCommentTemplatePaths"
|
||||
@select="insertSavedReply"
|
||||
/>
|
||||
<toolbar-more-dropdown data-testid="more" @execute="trackToolbarControlExecution" />
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export default () => {
|
|||
issuePath,
|
||||
registerPath,
|
||||
signInPath,
|
||||
newCommentTemplatePath,
|
||||
newCommentTemplatePaths,
|
||||
} = el.dataset;
|
||||
const router = createRouter(issuePath);
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ export default () => {
|
|||
issueIid,
|
||||
registerPath,
|
||||
signInPath,
|
||||
newCommentTemplatePath,
|
||||
newCommentTemplatePaths: JSON.parse(newCommentTemplatePaths),
|
||||
},
|
||||
mounted() {
|
||||
performanceMarkAndMeasure({
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ export default function initDiffsApp(store = notesStore) {
|
|||
const { dataset } = el;
|
||||
|
||||
Vue.use(VueApollo);
|
||||
const { newCommentTemplatePaths } = dataset;
|
||||
|
||||
const vm = new Vue({
|
||||
el,
|
||||
|
|
@ -27,7 +28,7 @@ export default function initDiffsApp(store = notesStore) {
|
|||
store,
|
||||
apolloProvider,
|
||||
provide: {
|
||||
newCommentTemplatePath: dataset.newCommentTemplatePath,
|
||||
newCommentTemplatePaths: newCommentTemplatePaths ? JSON.parse(newCommentTemplatePaths) : [],
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export const createTokenKeys = ({ disableReleaseFilter = false } = {}) => {
|
|||
type: 'string',
|
||||
param: 'title',
|
||||
symbol: '%',
|
||||
icon: 'clock',
|
||||
icon: 'milestone',
|
||||
tag: '%milestone',
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -52,6 +52,23 @@ export default {
|
|||
default: '',
|
||||
},
|
||||
},
|
||||
props: {
|
||||
buttonClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
variant: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'link',
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: __('Email a new %{name} to this project'),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
email: this.initialEmail,
|
||||
|
|
@ -90,8 +107,8 @@ export default {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<gl-button v-gl-modal="$options.modalId" variant="link"
|
||||
><gl-sprintf :message="__('Email a new %{name} to this project')"
|
||||
<gl-button v-gl-modal="$options.modalId" :class="buttonClass" :variant="variant"
|
||||
><gl-sprintf :message="text"
|
||||
><template #name>{{ issuableName }}</template></gl-sprintf
|
||||
></gl-button
|
||||
>
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<div ref="milestoneDetails" class="issue-milestone-details">
|
||||
<gl-icon :size="16" class="gl-mr-2 flex-shrink-0" name="clock" />
|
||||
<gl-icon :size="16" class="gl-mr-2 flex-shrink-0" name="milestone" />
|
||||
<span class="milestone-title gl-display-inline-block gl-text-truncate">{{
|
||||
milestone.title
|
||||
}}</span>
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ export default {
|
|||
{
|
||||
type: TOKEN_TYPE_MILESTONE,
|
||||
title: TOKEN_TITLE_MILESTONE,
|
||||
icon: 'clock',
|
||||
icon: 'milestone',
|
||||
token: MilestoneToken,
|
||||
fetchMilestones: this.fetchMilestones,
|
||||
recentSuggestionsStorageKey: 'dashboard-issues-recent-tokens-milestone',
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@ import { GlButton, GlDisclosureDropdown, GlEmptyState, GlLink, GlSprintf } from
|
|||
import { helpPagePath } from '~/helpers/help_page_helper';
|
||||
import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue';
|
||||
import NewResourceDropdown from '~/vue_shared/components/new_resource_dropdown/new_resource_dropdown.vue';
|
||||
import GitlabExperiment from '~/experimentation/components/gitlab_experiment.vue';
|
||||
import { i18n } from '../constants';
|
||||
import { hasNewIssueDropdown } from '../has_new_issue_dropdown_mixin';
|
||||
import EmptyStateWithoutAnyIssuesExperiment from './empty_state_without_any_issues_experiment.vue';
|
||||
|
||||
export default {
|
||||
i18n,
|
||||
|
|
@ -17,6 +19,8 @@ export default {
|
|||
GlLink,
|
||||
GlSprintf,
|
||||
NewResourceDropdown,
|
||||
GitlabExperiment,
|
||||
EmptyStateWithoutAnyIssuesExperiment,
|
||||
},
|
||||
mixins: [hasNewIssueDropdown()],
|
||||
inject: [
|
||||
|
|
@ -51,80 +55,102 @@ export default {
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
showIssuableByEmail: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="isSignedIn">
|
||||
<gl-empty-state
|
||||
:title="$options.i18n.noIssuesTitle"
|
||||
:svg-path="emptyStateSvgPath"
|
||||
:svg-height="150"
|
||||
data-testid="issuable-empty-state"
|
||||
>
|
||||
<template #description>
|
||||
<gl-link :href="$options.issuesHelpPagePath">
|
||||
{{ $options.i18n.noIssuesDescription }}
|
||||
</gl-link>
|
||||
<p v-if="canCreateProjects">
|
||||
<strong>{{ $options.i18n.noGroupIssuesSignedInDescription }}</strong>
|
||||
</p>
|
||||
</template>
|
||||
<template #actions>
|
||||
<gl-button
|
||||
v-if="canCreateProjects"
|
||||
:href="newProjectPath"
|
||||
variant="confirm"
|
||||
class="gl-mx-2 gl-mb-3"
|
||||
>
|
||||
{{ $options.i18n.newProjectLabel }}
|
||||
</gl-button>
|
||||
<gl-button
|
||||
v-if="showNewIssueLink"
|
||||
:href="newIssuePath"
|
||||
variant="confirm"
|
||||
class="gl-mx-2 gl-mb-3"
|
||||
>
|
||||
{{ $options.i18n.newIssueLabel }}
|
||||
</gl-button>
|
||||
|
||||
<gl-disclosure-dropdown
|
||||
v-if="showCsvButtons"
|
||||
class="gl-mx-2 gl-mb-3"
|
||||
:toggle-text="$options.i18n.importIssues"
|
||||
data-testid="import-issues-dropdown"
|
||||
>
|
||||
<csv-import-export-buttons
|
||||
:export-csv-path="exportCsvPathWithQuery"
|
||||
:issuable-count="currentTabCount"
|
||||
/>
|
||||
</gl-disclosure-dropdown>
|
||||
|
||||
<new-resource-dropdown
|
||||
v-if="showNewIssueDropdown"
|
||||
class="gl-align-self-center gl-mx-2 gl-mb-3"
|
||||
:query="$options.searchProjectsQuery"
|
||||
:query-variables="newIssueDropdownQueryVariables"
|
||||
:extract-projects="extractProjects"
|
||||
:group-id="groupId"
|
||||
<gitlab-experiment name="issues_mrs_empty_state">
|
||||
<template #candidate>
|
||||
<empty-state-without-any-issues-experiment
|
||||
:show-csv-buttons="showCsvButtons"
|
||||
:show-issuable-by-email="showIssuableByEmail"
|
||||
/>
|
||||
</template>
|
||||
</gl-empty-state>
|
||||
<hr />
|
||||
<p class="gl-text-center gl-font-weight-bold gl-mb-0">
|
||||
{{ $options.i18n.jiraIntegrationTitle }}
|
||||
</p>
|
||||
<p class="gl-text-center gl-mb-0">
|
||||
<gl-sprintf :message="$options.i18n.jiraIntegrationMessage">
|
||||
<template #jiraDocsLink="{ content }">
|
||||
<gl-link :href="jiraIntegrationPath">{{ content }}</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
<p class="gl-text-center gl-text-secondary">
|
||||
{{ $options.i18n.jiraIntegrationSecondaryMessage }}
|
||||
</p>
|
||||
|
||||
<template #control>
|
||||
<div>
|
||||
<gl-empty-state
|
||||
:title="$options.i18n.noIssuesTitle"
|
||||
:svg-path="emptyStateSvgPath"
|
||||
:svg-height="150"
|
||||
data-testid="issuable-empty-state"
|
||||
>
|
||||
<template #description>
|
||||
<gl-link :href="$options.issuesHelpPagePath">
|
||||
{{ $options.i18n.noIssuesDescription }}
|
||||
</gl-link>
|
||||
<p v-if="canCreateProjects">
|
||||
<strong>{{ $options.i18n.noGroupIssuesSignedInDescription }}</strong>
|
||||
</p>
|
||||
</template>
|
||||
<template #actions>
|
||||
<!-- This component is shared between groups and projects issues list
|
||||
Now issues_mrs_empty_state experiment is run only for projects page
|
||||
If this experiment is successful new project buttons from 'control' should be
|
||||
moved to 'candidate' template to take affect for groups page as well -->
|
||||
<gl-button
|
||||
v-if="canCreateProjects"
|
||||
:href="newProjectPath"
|
||||
variant="confirm"
|
||||
class="gl-mx-2 gl-mb-3"
|
||||
>
|
||||
{{ $options.i18n.newProjectLabel }}
|
||||
</gl-button>
|
||||
<gl-button
|
||||
v-if="showNewIssueLink"
|
||||
:href="newIssuePath"
|
||||
variant="confirm"
|
||||
class="gl-mx-2 gl-mb-3"
|
||||
>
|
||||
{{ $options.i18n.newIssueLabel }}
|
||||
</gl-button>
|
||||
|
||||
<gl-disclosure-dropdown
|
||||
v-if="showCsvButtons"
|
||||
class="gl-mx-2 gl-mb-3"
|
||||
:toggle-text="$options.i18n.importIssues"
|
||||
data-testid="import-issues-dropdown"
|
||||
>
|
||||
<csv-import-export-buttons
|
||||
:export-csv-path="exportCsvPathWithQuery"
|
||||
:issuable-count="currentTabCount"
|
||||
/>
|
||||
</gl-disclosure-dropdown>
|
||||
|
||||
<new-resource-dropdown
|
||||
v-if="showNewIssueDropdown"
|
||||
class="gl-align-self-center gl-mx-2 gl-mb-3"
|
||||
:query="$options.searchProjectsQuery"
|
||||
:query-variables="newIssueDropdownQueryVariables"
|
||||
:extract-projects="extractProjects"
|
||||
:group-id="groupId"
|
||||
/>
|
||||
</template>
|
||||
</gl-empty-state>
|
||||
<hr />
|
||||
<p class="gl-text-center gl-font-weight-bold gl-mb-0">
|
||||
{{ $options.i18n.jiraIntegrationTitle }}
|
||||
</p>
|
||||
<p class="gl-text-center gl-mb-0">
|
||||
<gl-sprintf :message="$options.i18n.jiraIntegrationMessage">
|
||||
<template #jiraDocsLink="{ content }">
|
||||
<gl-link :href="jiraIntegrationPath">{{ content }}</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
<p class="gl-text-center gl-text-secondary">
|
||||
{{ $options.i18n.jiraIntegrationSecondaryMessage }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</gitlab-experiment>
|
||||
</div>
|
||||
|
||||
<gl-empty-state
|
||||
|
|
|
|||
|
|
@ -0,0 +1,180 @@
|
|||
<script>
|
||||
import { GlButton, GlModalDirective, GlIcon } from '@gitlab/ui';
|
||||
import { TYPE_ISSUE } from '~/issues/constants';
|
||||
import { helpPagePath } from '~/helpers/help_page_helper';
|
||||
import IssuableByEmail from '~/issuable/components/issuable_by_email.vue';
|
||||
import CsvImportModal from '~/issuable/components/csv_import_modal.vue';
|
||||
import { i18n } from '../constants';
|
||||
import GlCardEmptyStateExperiment from './gl_card_empty_state_experiment.vue';
|
||||
|
||||
export default {
|
||||
i18n,
|
||||
issuesHelpPagePath: helpPagePath('user/project/issues/index'),
|
||||
components: {
|
||||
GlCardEmptyStateExperiment,
|
||||
GlButton,
|
||||
GlIcon,
|
||||
IssuableByEmail,
|
||||
CsvImportModal,
|
||||
},
|
||||
directives: {
|
||||
GlModal: GlModalDirective,
|
||||
},
|
||||
inject: {
|
||||
jiraIntegrationPath: {
|
||||
default: null,
|
||||
},
|
||||
newIssuePath: {
|
||||
default: null,
|
||||
},
|
||||
showNewIssueLink: {
|
||||
default: false,
|
||||
},
|
||||
showImportButton: {
|
||||
default: false,
|
||||
},
|
||||
canEdit: {
|
||||
default: false,
|
||||
},
|
||||
projectImportJiraPath: {
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
showCsvButtons: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
showIssuableByEmail: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
importModalId() {
|
||||
return `${TYPE_ISSUE}-import-modal`;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="gl-text-center gl-empty-state">
|
||||
<iframe
|
||||
class="video-container gl-h-auto gl-w-full"
|
||||
src="https://www.youtube-nocookie.com/embed/-4SGhGpwZDY?si=Ym-29hpY22cgdGdH"
|
||||
title="Create an Issue - The basics"
|
||||
frameborder="0"
|
||||
allow="accelerometer; encrypted-media; gyroscope;"
|
||||
sandbox="allow-scripts allow-same-origin allow-presentation"
|
||||
data-testid="create-an-issue-iframe-video"
|
||||
allowfullscreen
|
||||
></iframe>
|
||||
|
||||
<h1 class="gl-font-size-h-display gl-max-w-75 gl-m-auto gl-pt-8">
|
||||
{{ $options.i18n.noIssuesTitle }}
|
||||
</h1>
|
||||
|
||||
<p class="gl-max-w-75 gl-m-auto gl-pt-4 gl-pb-5">
|
||||
{{
|
||||
__(
|
||||
'With issues you can discuss the implementation of an idea, track tasks and work status, elaborate on code implementations, and accept feature proposals, questions, support requests, or bug reports.',
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
|
||||
<div
|
||||
class="gl-display-flex gl-flex-direction-column gl-sm-flex-direction-row gl-justify-content-center gl-gap-3"
|
||||
>
|
||||
<gl-button
|
||||
v-if="showNewIssueLink"
|
||||
:href="newIssuePath"
|
||||
variant="confirm"
|
||||
data-testid="empty-state-new-issue-btn"
|
||||
>
|
||||
{{ __('Create a new issue') }}
|
||||
</gl-button>
|
||||
|
||||
<issuable-by-email
|
||||
v-if="showIssuableByEmail"
|
||||
button-class="gl-w-full"
|
||||
variant="default"
|
||||
:text="__('Email a new issue')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="gl-display-flex gl-flex-direction-column gl-gap-6 gl-max-w-88 gl-mx-auto gl-pt-9">
|
||||
<div
|
||||
class="gl-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-justify-content-center gl-gap-6"
|
||||
>
|
||||
<gl-card-empty-state-experiment v-if="showCsvButtons && showImportButton" icon="download">
|
||||
<template #header>
|
||||
{{ __('Import existing issues') }}
|
||||
</template>
|
||||
<template #body>
|
||||
<csv-import-modal :modal-id="importModalId" />
|
||||
|
||||
<div class="gl-display-flex gl-flex-direction-column gl-sm-flex-direction-row gl-gap-3">
|
||||
<gl-button
|
||||
v-gl-modal="importModalId"
|
||||
class="gl-mt-3 gl-ml-0! gl-mb-0!"
|
||||
data-testid="empty-state-import-csv-btn"
|
||||
>
|
||||
{{ __('Import CSV') }}
|
||||
</gl-button>
|
||||
|
||||
<gl-button
|
||||
v-if="canEdit"
|
||||
class="gl-mt-3 gl-mb-0!"
|
||||
:href="projectImportJiraPath"
|
||||
data-testid="empty-state-import-jira-btn"
|
||||
>
|
||||
{{ __('Import from Jira') }}
|
||||
</gl-button>
|
||||
</div>
|
||||
</template>
|
||||
</gl-card-empty-state-experiment>
|
||||
|
||||
<a class="gl-text-decoration-none!" :href="$options.issuesHelpPagePath">
|
||||
<gl-card-empty-state-experiment
|
||||
class="gl-h-13 gl-justify-content-center gl-hover-text-blue-600 gl-text-gray-900"
|
||||
icon="issue-type-issue"
|
||||
>
|
||||
<template #header>
|
||||
{{ __('Learn more about issues') }}
|
||||
<gl-icon class="gl-display-inline gl-ml-2" name="arrow-right" />
|
||||
</template>
|
||||
|
||||
<template #body>
|
||||
<span class="gl-text-gray-900">{{ __('Read our documentation') }}</span>
|
||||
</template>
|
||||
</gl-card-empty-state-experiment>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="gl-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-justify-content-center"
|
||||
>
|
||||
<a class="gl-text-decoration-none!" :href="jiraIntegrationPath">
|
||||
<gl-card-empty-state-experiment
|
||||
class="gl-h-13 gl-hover-text-blue-600 gl-text-gray-900"
|
||||
icon="api"
|
||||
>
|
||||
<template #header>
|
||||
{{ __('Enable Jira integration') }}
|
||||
<gl-icon class="gl-display-inline gl-ml-2" name="arrow-right" />
|
||||
</template>
|
||||
|
||||
<template #body>
|
||||
<span class="gl-text-gray-900">
|
||||
{{ __('This feature is only available on paid plans.') }}
|
||||
</span>
|
||||
</template>
|
||||
</gl-card-empty-state-experiment>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<script>
|
||||
import { GlCard, GlIcon } from '@gitlab/ui';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlCard,
|
||||
GlIcon,
|
||||
},
|
||||
props: {
|
||||
icon: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-card class="gl-text-left gl-hover-bg-blue-50">
|
||||
<div class="gl-display-flex gl-gap-4">
|
||||
<div class="gl-display-flex gl-my-auto gl-bg-blue-100 gl-rounded-full gl-min-w-8 gl-min-h-8">
|
||||
<gl-icon class="gl-display-flex gl-m-auto gl-text-gray-900" :name="icon" />
|
||||
</div>
|
||||
|
||||
<div class="gl-w-31">
|
||||
<p class="gl-heading-4 gl-mb-1!">
|
||||
<slot name="header"></slot>
|
||||
</p>
|
||||
|
||||
<slot name="body"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</gl-card>
|
||||
</template>
|
||||
|
|
@ -95,7 +95,7 @@ export default {
|
|||
:title="milestoneDate"
|
||||
class="gl-font-sm gl-text-gray-500!"
|
||||
>
|
||||
<gl-icon name="clock" :size="12" />
|
||||
<gl-icon name="milestone" :size="12" />
|
||||
{{ milestone.title }}
|
||||
</gl-link>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ import NewResourceDropdown from '~/vue_shared/components/new_resource_dropdown/n
|
|||
import WorkItemDetail from '~/work_items/components/work_item_detail.vue';
|
||||
import deleteWorkItemMutation from '~/work_items/graphql/delete_work_item.mutation.graphql';
|
||||
import { WORK_ITEM_TYPE_ENUM_OBJECTIVE } from '~/work_items/constants';
|
||||
import GitlabExperiment from '~/experimentation/components/gitlab_experiment.vue';
|
||||
import {
|
||||
CREATED_DESC,
|
||||
defaultTypeTokenOptions,
|
||||
|
|
@ -153,6 +154,7 @@ export default {
|
|||
LocalStorageSync,
|
||||
WorkItemDetail,
|
||||
GlLink,
|
||||
GitlabExperiment,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
|
|
@ -403,7 +405,7 @@ export default {
|
|||
{
|
||||
type: TOKEN_TYPE_MILESTONE,
|
||||
title: TOKEN_TITLE_MILESTONE,
|
||||
icon: 'clock',
|
||||
icon: 'milestone',
|
||||
token: MilestoneToken,
|
||||
recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-milestone`,
|
||||
shouldSkipSort: true,
|
||||
|
|
@ -1073,8 +1075,13 @@ export default {
|
|||
:export-csv-path-with-query="exportCsvPathWithQuery"
|
||||
:show-csv-buttons="showCsvButtons"
|
||||
:show-new-issue-dropdown="showNewIssueDropdown"
|
||||
:show-issuable-by-email="showIssuableByEmail"
|
||||
/>
|
||||
|
||||
<issuable-by-email v-if="showIssuableByEmail" class="gl-text-center gl-pt-5 gl-pb-7" />
|
||||
<gitlab-experiment v-if="showIssuableByEmail" name="issues_mrs_empty_state">
|
||||
<template #control>
|
||||
<issuable-by-email class="gl-text-center gl-pt-5 gl-pb-7" />
|
||||
</template>
|
||||
</gitlab-experiment>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ export const assigneeTokenBase = {
|
|||
export const milestoneTokenBase = {
|
||||
type: TOKEN_TYPE_MILESTONE,
|
||||
title: TOKEN_TITLE_MILESTONE,
|
||||
icon: 'clock',
|
||||
icon: 'milestone',
|
||||
token: MilestoneToken,
|
||||
shouldSkipSort: true,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export default () => {
|
|||
apolloProvider,
|
||||
provide: {
|
||||
reportAbusePath: notesDataset.reportAbusePath,
|
||||
newCommentTemplatePath: notesDataset.newCommentTemplatePath,
|
||||
newCommentTemplatePaths: JSON.parse(notesDataset.newCommentTemplatePaths),
|
||||
mrFilter: true,
|
||||
newCustomEmojiPath: notesDataset.newCustomEmojiPath,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ export default ({ editorAiActions = [] } = {}) => {
|
|||
provide: {
|
||||
showTimelineViewToggle,
|
||||
reportAbusePath: notesDataset.reportAbusePath,
|
||||
newCommentTemplatePath: notesDataset.newCommentTemplatePath,
|
||||
newCommentTemplatePaths: JSON.parse(notesDataset.newCommentTemplatePaths),
|
||||
resourceGlobalId: convertToGraphQLId(noteableData.noteableType, noteableData.id),
|
||||
editorAiActions: editorAiActions.map((factory) => factory(noteableData)),
|
||||
newCustomEmojiPath: notesDataset.newCustomEmojiPath,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export const ICON_MAP = {
|
|||
merge_requests: 'merge-request',
|
||||
commits: 'commit',
|
||||
notes: 'comments',
|
||||
milestones: 'clock',
|
||||
milestones: 'milestone',
|
||||
users: 'users',
|
||||
projects: 'project',
|
||||
wiki_blobs: 'book',
|
||||
|
|
|
|||
|
|
@ -272,7 +272,7 @@ function mountSidebarMilestoneWidget() {
|
|||
iid: issueIid,
|
||||
issuableType: isInIssuePage() || isInDesignPage() ? TYPE_ISSUE : TYPE_MERGE_REQUEST,
|
||||
issuableAttribute: IssuableAttributeType.Milestone,
|
||||
icon: 'clock',
|
||||
icon: 'milestone',
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ export default {
|
|||
},
|
||||
mixins: [InternalEvents.mixin()],
|
||||
props: {
|
||||
newCommentTemplatePath: {
|
||||
type: String,
|
||||
newCommentTemplatePaths: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
|
@ -147,14 +147,17 @@ export default {
|
|||
</template>
|
||||
<template #footer>
|
||||
<div
|
||||
class="gl-border-t-solid gl-border-t-1 gl-border-t-gray-200 gl-display-flex gl-justify-content-center gl-p-2"
|
||||
class="gl-border-t-solid gl-border-t-1 gl-border-t-gray-200 gl-display-flex gl-justify-content-center gl-flex-direction-column gl-p-2"
|
||||
>
|
||||
<gl-button
|
||||
:href="newCommentTemplatePath"
|
||||
v-for="(manage, index) in newCommentTemplatePaths"
|
||||
:key="index"
|
||||
:href="manage.path"
|
||||
category="tertiary"
|
||||
block
|
||||
class="gl-justify-content-start! gl-mt-0! gl-mb-0! gl-px-3!"
|
||||
>{{ __('Add a new comment template') }}</gl-button
|
||||
data-testid="manage-button"
|
||||
>{{ manage.text }}</gl-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -167,7 +170,7 @@ export default {
|
|||
|
||||
<style>
|
||||
.comment-template-dropdown .gl-new-dropdown-panel {
|
||||
width: 350px;
|
||||
width: 350px !important;
|
||||
}
|
||||
|
||||
.comment-template-dropdown .gl-new-dropdown-item-check-icon {
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ export default {
|
|||
},
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
inject: {
|
||||
newCommentTemplatePath: {
|
||||
default: null,
|
||||
newCommentTemplatePaths: {
|
||||
default: () => [],
|
||||
},
|
||||
editorAiActions: { default: () => [] },
|
||||
mrGeneratedContent: { default: null },
|
||||
|
|
@ -512,8 +512,8 @@ export default {
|
|||
tracking-property="quickAction"
|
||||
/>
|
||||
<comment-templates-dropdown
|
||||
v-if="!previewMarkdown && newCommentTemplatePath"
|
||||
:new-comment-template-path="newCommentTemplatePath"
|
||||
v-if="!previewMarkdown && newCommentTemplatePaths.length"
|
||||
:new-comment-template-paths="newCommentTemplatePaths"
|
||||
@select="insertSavedReply"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export const initWorkItemsRoot = ({ workItemType, workspaceType } = {}) => {
|
|||
hasIterationsFeature,
|
||||
hasOkrsFeature,
|
||||
hasIssuableHealthStatusFeature,
|
||||
newCommentTemplatePath,
|
||||
newCommentTemplatePaths,
|
||||
reportAbusePath,
|
||||
} = el.dataset;
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ export const initWorkItemsRoot = ({ workItemType, workspaceType } = {}) => {
|
|||
signInPath,
|
||||
hasIterationsFeature: parseBoolean(hasIterationsFeature),
|
||||
hasIssuableHealthStatusFeature: parseBoolean(hasIssuableHealthStatusFeature),
|
||||
newCommentTemplatePath,
|
||||
newCommentTemplatePaths: JSON.parse(newCommentTemplatePaths),
|
||||
reportAbusePath,
|
||||
},
|
||||
render(createElement) {
|
||||
|
|
|
|||
|
|
@ -106,3 +106,10 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gl-empty-state {
|
||||
.video-container {
|
||||
aspect-ratio: 16/9;
|
||||
max-width: 350px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,9 +38,10 @@ class Admin::GroupsController < Admin::ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
@group = ::Groups::CreateService.new(current_user, group_params).execute
|
||||
response = ::Groups::CreateService.new(current_user, group_params).execute
|
||||
@group = response[:group]
|
||||
|
||||
if @group.persisted?
|
||||
if response.success?
|
||||
redirect_to [:admin, @group], notice: format(_('Group %{group_name} was successfully created.'), group_name: @group.name)
|
||||
else
|
||||
render "new"
|
||||
|
|
|
|||
|
|
@ -85,9 +85,10 @@ class GroupsController < Groups::ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
@group = Groups::CreateService.new(current_user, group_params).execute
|
||||
response = Groups::CreateService.new(current_user, group_params).execute
|
||||
@group = response[:group]
|
||||
|
||||
if @group.persisted?
|
||||
if response.success?
|
||||
successful_creation_hooks
|
||||
|
||||
notice = if @group.chat_team.present?
|
||||
|
|
|
|||
|
|
@ -18,9 +18,10 @@ class Import::GitlabGroupsController < ApplicationController
|
|||
import_export_upload: ImportExportUpload.new(import_file: group_params[:file])
|
||||
)
|
||||
|
||||
group = ::Groups::CreateService.new(current_user, group_data).execute
|
||||
response = ::Groups::CreateService.new(current_user, group_data).execute
|
||||
group = response[:group]
|
||||
|
||||
if group.persisted?
|
||||
if response.success?
|
||||
if Groups::ImportExport::ImportService.new(group: group, user: current_user).async_execute
|
||||
redirect_to(
|
||||
group_path(group),
|
||||
|
|
|
|||
|
|
@ -26,8 +26,6 @@ class SessionsController < Devise::SessionsController
|
|||
prepend_before_action :check_captcha, only: [:create]
|
||||
prepend_before_action :store_redirect_uri, only: [:new]
|
||||
prepend_before_action :require_no_authentication_without_flash, only: [:new, :create]
|
||||
prepend_before_action :ensure_user_allowed_to_password_authenticate,
|
||||
if: -> { action_name == 'create' && password_based_login? }
|
||||
prepend_before_action :ensure_password_authentication_enabled!,
|
||||
if: -> { action_name == 'create' && password_based_login? }
|
||||
before_action :auto_sign_in_with_provider, only: [:new]
|
||||
|
|
@ -316,13 +314,6 @@ class SessionsController < Devise::SessionsController
|
|||
def set_invite_params
|
||||
@invite_email = ActionController::Base.helpers.sanitize(params[:invite_email])
|
||||
end
|
||||
|
||||
def ensure_user_allowed_to_password_authenticate
|
||||
return unless find_user
|
||||
return if find_user.allow_password_authentication_for_web? && !find_user.password_based_login_forbidden?
|
||||
|
||||
redirect_to new_user_session_path, alert: I18n.t('devise.failure.invalid')
|
||||
end
|
||||
end
|
||||
|
||||
SessionsController.prepend_mod_with('SessionsController')
|
||||
|
|
|
|||
|
|
@ -417,6 +417,13 @@ module IssuablesHelper
|
|||
}
|
||||
}
|
||||
end
|
||||
|
||||
def new_comment_template_paths(group)
|
||||
[{
|
||||
text: _('Manage your comment templates'),
|
||||
path: profile_comment_templates_path
|
||||
}]
|
||||
end
|
||||
end
|
||||
|
||||
IssuablesHelper.prepend_mod_with('IssuablesHelper')
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ module MergeRequestsHelper
|
|||
source_project_default_url: merge_request.source_project && default_url_to_repo(merge_request.source_project),
|
||||
source_project_full_path: merge_request.source_project&.full_path,
|
||||
is_forked: project.forked?.to_s,
|
||||
new_comment_template_path: profile_comment_templates_path,
|
||||
new_comment_template_paths: new_comment_template_paths(project.group).to_json,
|
||||
iid: merge_request.iid,
|
||||
per_page: DIFF_BATCH_ENDPOINT_PER_PAGE,
|
||||
pinned_file_url: @pinned_file_url
|
||||
|
|
@ -315,7 +315,7 @@ module MergeRequestsHelper
|
|||
end
|
||||
|
||||
def review_bar_data(_merge_request, _user)
|
||||
{ new_comment_template_path: profile_comment_templates_path }
|
||||
{ new_comment_template_paths: new_comment_template_paths(@project.group).to_json }
|
||||
end
|
||||
|
||||
def project_merge_requests_list_data(project, current_user)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ module SystemNoteHelper
|
|||
'branch' => 'fork',
|
||||
'confidential' => 'eye-slash',
|
||||
'visible' => 'eye',
|
||||
'milestone' => 'clock',
|
||||
'milestone' => 'milestone',
|
||||
'discussion' => 'comment',
|
||||
'moved' => 'arrow-right',
|
||||
'outdated' => 'pencil',
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@
|
|||
|
||||
module WorkItemsHelper
|
||||
def work_items_show_data(resource_parent)
|
||||
group = resource_parent.is_a?(Group) ? resource_parent : resource_parent.group
|
||||
|
||||
{
|
||||
full_path: resource_parent.full_path,
|
||||
issues_list_path:
|
||||
resource_parent.is_a?(Group) ? issues_group_path(resource_parent) : project_issues_path(resource_parent),
|
||||
register_path: new_user_registration_path(redirect_to_referer: 'yes'),
|
||||
sign_in_path: new_session_path(:user, redirect_to_referer: 'yes'),
|
||||
new_comment_template_path: profile_comment_templates_path,
|
||||
new_comment_template_paths: new_comment_template_paths(group).to_json,
|
||||
report_abuse_path: add_category_abuse_reports_path
|
||||
}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ class Commit
|
|||
MIN_SHA_LENGTH = Gitlab::Git::Commit::MIN_SHA_LENGTH
|
||||
MAX_SHA_LENGTH = Gitlab::Git::Commit::MAX_SHA_LENGTH
|
||||
COMMIT_SHA_PATTERN = Gitlab::Git::Commit::SHA_PATTERN
|
||||
WHOLE_WORD_COMMIT_SHA_PATTERN = /\b#{COMMIT_SHA_PATTERN}\b/
|
||||
EXACT_COMMIT_SHA_PATTERN = /\A#{COMMIT_SHA_PATTERN}\z/
|
||||
# Used by GFM to match and present link extensions on node texts and hrefs.
|
||||
LINK_EXTENSION_PATTERN = /(patch)/
|
||||
|
|
@ -205,7 +206,7 @@ class Commit
|
|||
def self.reference_pattern
|
||||
@reference_pattern ||= %r{
|
||||
(?:#{Project.reference_pattern}#{reference_prefix})?
|
||||
(?<commit>#{COMMIT_SHA_PATTERN})
|
||||
(?<commit>#{WHOLE_WORD_COMMIT_SHA_PATTERN})
|
||||
}x
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1058,6 +1058,7 @@ class User < MainClusterwide::ApplicationRecord
|
|||
def valid_password?(password)
|
||||
return false unless password_allowed?(password)
|
||||
return false if password_automatically_set?
|
||||
return false unless allow_password_authentication?
|
||||
|
||||
super
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
module Groups
|
||||
class CreateService < Groups::BaseService
|
||||
VisibilityError = Class.new(StandardError)
|
||||
PermissionError = Class.new(StandardError)
|
||||
|
||||
def initialize(user, params = {})
|
||||
@current_user = user
|
||||
@params = params.dup
|
||||
|
|
@ -10,6 +13,43 @@ module Groups
|
|||
end
|
||||
|
||||
def execute
|
||||
build_group
|
||||
after_build_hook
|
||||
|
||||
validate_visibility_level!
|
||||
validate_user_permissions!
|
||||
|
||||
@group.name ||= @group.path.dup
|
||||
|
||||
create_chat_team
|
||||
create_group
|
||||
|
||||
if @group.persisted?
|
||||
after_successful_creation_hook
|
||||
|
||||
ServiceResponse.success(payload: { group: @group })
|
||||
else
|
||||
ServiceResponse.error(message: 'Group has errors', payload: { group: @group })
|
||||
end
|
||||
|
||||
rescue VisibilityError, PermissionError
|
||||
ServiceResponse.error(message: 'Group has errors', payload: { group: @group })
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :create_event
|
||||
|
||||
def create_chat_team
|
||||
return unless valid_to_create_chat_team?
|
||||
|
||||
response = ::Mattermost::CreateTeamService.new(@group, current_user).execute
|
||||
return ServiceResponse.error(message: 'Group has errors', payload: { group: @group }) if @group.errors.any?
|
||||
|
||||
@group.build_chat_team(name: response['name'], team_id: response['id'])
|
||||
end
|
||||
|
||||
def build_group
|
||||
remove_unallowed_params
|
||||
set_visibility_level
|
||||
|
||||
|
|
@ -19,24 +59,9 @@ module Groups
|
|||
|
||||
@group.build_namespace_settings
|
||||
handle_namespace_settings
|
||||
end
|
||||
|
||||
after_build_hook(@group, params)
|
||||
|
||||
inherit_group_shared_runners_settings
|
||||
|
||||
unless can_use_visibility_level? && can_create_group?
|
||||
return @group
|
||||
end
|
||||
|
||||
@group.name ||= @group.path.dup
|
||||
|
||||
if create_chat_team?
|
||||
response = ::Mattermost::CreateTeamService.new(@group, current_user).execute
|
||||
return @group if @group.errors.any?
|
||||
|
||||
@group.build_chat_team(name: response['name'], team_id: response['id'])
|
||||
end
|
||||
|
||||
def create_group
|
||||
Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification.temporary_ignore_tables_in_transaction(
|
||||
%w[routes redirect_routes], url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/424281'
|
||||
) do
|
||||
|
|
@ -47,21 +72,13 @@ module Groups
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
after_create_hook
|
||||
|
||||
@group
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :create_event
|
||||
|
||||
def after_build_hook(group, params)
|
||||
# overridden in EE
|
||||
def after_build_hook
|
||||
inherit_group_shared_runners_settings
|
||||
end
|
||||
|
||||
def after_create_hook
|
||||
def after_successful_creation_hook
|
||||
# overridden in EE
|
||||
end
|
||||
|
||||
|
|
@ -76,35 +93,33 @@ module Groups
|
|||
params.delete(:lock_math_rendering_limits_enabled)
|
||||
end
|
||||
|
||||
def create_chat_team?
|
||||
Gitlab.config.mattermost.enabled && @chat_team && group.chat_team.nil?
|
||||
def valid_to_create_chat_team?
|
||||
Gitlab.config.mattermost.enabled && @chat_team && @group.chat_team.nil?
|
||||
end
|
||||
|
||||
def can_create_group?
|
||||
def validate_user_permissions!
|
||||
if @group.subgroup?
|
||||
unless can?(current_user, :create_subgroup, @group.parent)
|
||||
@group.parent = nil
|
||||
@group.errors.add(:parent_id, s_('CreateGroup|You don’t have permission to create a subgroup in this group.'))
|
||||
|
||||
return false
|
||||
raise PermissionError
|
||||
end
|
||||
else
|
||||
unless can?(current_user, :create_group)
|
||||
@group.errors.add(:base, s_('CreateGroup|You don’t have permission to create groups.'))
|
||||
|
||||
return false
|
||||
raise PermissionError
|
||||
end
|
||||
end
|
||||
|
||||
unless organization_setting_valid?
|
||||
# We are unsetting this here to match behavior of invalid parent_id above and protect against possible
|
||||
# committing to the database of a value that isn't allowed.
|
||||
@group.organization = nil
|
||||
return if organization_setting_valid?
|
||||
|
||||
return false
|
||||
end
|
||||
# We are unsetting this here to match behavior of invalid parent_id above and protect against possible
|
||||
# committing to the database of a value that isn't allowed.
|
||||
@group.organization = nil
|
||||
|
||||
true
|
||||
raise PermissionError
|
||||
end
|
||||
|
||||
def can_create_group_in_organization?
|
||||
|
|
@ -143,13 +158,12 @@ module Groups
|
|||
can_create_group_in_organization? && matches_parent_organization?
|
||||
end
|
||||
|
||||
def can_use_visibility_level?
|
||||
unless Gitlab::VisibilityLevel.allowed_for?(current_user, visibility_level)
|
||||
deny_visibility_level(@group)
|
||||
return false
|
||||
end
|
||||
def validate_visibility_level!
|
||||
return if Gitlab::VisibilityLevel.allowed_for?(current_user, visibility_level)
|
||||
|
||||
true
|
||||
deny_visibility_level(@group)
|
||||
|
||||
raise VisibilityError, 'Visibility level not allowed'
|
||||
end
|
||||
|
||||
def set_visibility_level
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ module Groups
|
|||
)
|
||||
|
||||
last_group = namespace_or_group(partial_path) ||
|
||||
Groups::CreateService.new(current_user, new_params).execute
|
||||
Groups::CreateService.new(current_user, new_params).execute[:group]
|
||||
end
|
||||
|
||||
last_group
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
- page_title _('CI/CD Catalog')
|
||||
|
||||
#js-ci-cd-catalog{ data: { ci_catalog_path: explore_catalog_index_path } }
|
||||
#js-ci-cd-catalog{ data: { ci_catalog_path: explore_catalog_index_path, report_abuse_path: add_category_abuse_reports_path } }
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
%span.issuable-milestone.gl-display-none.gl-sm-display-inline-block
|
||||
|
||||
= link_to project_issues_path(issue.project, milestone_title: issue.milestone.title), data: { html: 'true', toggle: 'tooltip', title: milestone_tooltip_due_date(issue.milestone) } do
|
||||
= sprite_icon('clock', css_class: 'gl-vertical-align-text-bottom')
|
||||
= sprite_icon('milestone', css_class: 'gl-vertical-align-text-bottom')
|
||||
= issue.milestone.title
|
||||
- if issue.due_date
|
||||
%span.issuable-due-date.gl-display-none.gl-sm-display-inline-block.has-tooltip{ class: "#{'cred' if issue.overdue? && !issue.closed?}", title: _('Due date') }
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
issue_path: project_issue_path(@project, @issue),
|
||||
register_path: new_user_registration_path(redirect_to_referer: 'yes'),
|
||||
sign_in_path: new_session_path(:user, redirect_to_referer: 'yes'),
|
||||
new_comment_template_path: profile_comment_templates_path } }
|
||||
new_comment_template_paths: new_comment_template_paths(@project.group).to_json } }
|
||||
- else
|
||||
.gl-border-solid.gl-border-1.gl-border-gray-100.gl-rounded-base.gl-mt-5.gl-p-3.gl-text-center
|
||||
= enable_lfs_message
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@
|
|||
current_user_data: UserSerializer.new.represent(current_user, {only_path: true}, CurrentUserEntity).to_json,
|
||||
can_add_timeline_events: "#{can?(current_user, :admin_incident_management_timeline_event, @issue)}",
|
||||
report_abuse_path: add_category_abuse_reports_path,
|
||||
new_comment_template_path: profile_comment_templates_path,
|
||||
new_comment_template_paths: new_comment_template_paths(@project.group).to_json,
|
||||
new_custom_emoji_path: new_custom_emoji_path(@project.group) } }
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
%span.issuable-milestone.gl-display-none.gl-sm-display-inline-block.gl-text-truncate.gl-max-w-26.gl-vertical-align-bottom
|
||||
|
||||
= link_to project_merge_requests_path(merge_request.project, milestone_title: merge_request.milestone.title), class: 'gl-text-gray-500!', data: { html: 'true', toggle: 'tooltip', title: milestone_tooltip_due_date(merge_request.milestone) } do
|
||||
= sprite_icon('clock', size: 12, css_class: 'gl-vertical-align-text-bottom')
|
||||
= sprite_icon('milestone', size: 12, css_class: 'gl-vertical-align-text-bottom')
|
||||
= merge_request.milestone.title
|
||||
- if merge_request.target_project.default_branch != merge_request.target_branch
|
||||
%span.project-ref-path.has-tooltip.d-inline-block.gl-text-truncate.gl-max-w-26.gl-vertical-align-bottom{ title: _('Target branch: %{target_branch}') % {target_branch: merge_request.target_branch} }
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@
|
|||
current_user_data: @current_user_data,
|
||||
is_locked: @merge_request.discussion_locked.to_s,
|
||||
report_abuse_path: add_category_abuse_reports_path,
|
||||
new_comment_template_path: profile_comment_templates_path,
|
||||
new_comment_template_paths: new_comment_template_paths(@project.group).to_json,
|
||||
new_custom_emoji_path: new_custom_emoji_path(@project.group) } }
|
||||
= render 'shared/issuable/sidebar', issuable_sidebar: @issuable_sidebar, assignees: @merge_request.assignees, reviewers: @merge_request.reviewers, source_branch: @merge_request.source_branch
|
||||
|
||||
|
|
|
|||
|
|
@ -18,9 +18,8 @@
|
|||
= sprite_icon('comments', css_class: 'gl-vertical-align-text-bottom')
|
||||
= notes_count
|
||||
%li
|
||||
%span.sr-only{ data: { testid: 'snippet-visibility-content', qa_snippet_visibility: visibility_level_label(snippet.visibility_level) } }
|
||||
= visibility_level_label(snippet.visibility_level)
|
||||
= visibility_level_icon(snippet.visibility_level)
|
||||
%span.has-tooltip{ title: visibility_level_label(snippet.visibility_level), data: { testid: 'snippet-visibility-content', qa_snippet_visibility: visibility_level_label(snippet.visibility_level) } }
|
||||
= visibility_level_icon(snippet.visibility_level)
|
||||
|
||||
.snippet-info
|
||||
.gl-display-inline{ data: { testid: 'snippet-created-at'} }
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
name: ci_fix_input_types
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/434826
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/145257
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/443301
|
||||
milestone: '16.10'
|
||||
group: group::pipeline authoring
|
||||
type: gitlab_com_derisk
|
||||
default_enabled: false
|
||||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_verify_load_performance
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_verify_accessibility_mo
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_verify_failfast_monthly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_verify_browser_performa
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_verify_browser_performa
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_security_secure_binarie
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: static_analysis
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_load_performance_t
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_secret_detection_m
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: static_analysis
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_browser_performanc
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_browser_performanc
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_load_perf
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: removed
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_browser_p
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_browser_p
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: removed
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_security_secur
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: static_analysis
|
||||
value_type: number
|
||||
status: removed
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_qualys_iac_security_mon
|
|||
description: ""
|
||||
product_section: ""
|
||||
product_stage: ""
|
||||
product_group: ""
|
||||
product_group: static_analysis
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: "14.7"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_verify_load_performance
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_verify_accessibility_we
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_verify_failfast_weekly
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_verify_browser_performa
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_verify_browser_performa
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_security_secure_binarie
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: static_analysis
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_load_performance_t
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_secret_detection_w
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: static_analysis
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_browser_performanc
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_browser_performanc
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_load_perf
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: removed
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_browser_p
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_browser_p
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: pipeline_execution
|
||||
value_type: number
|
||||
status: removed
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_security_secur
|
|||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_group: static_analysis
|
||||
value_type: number
|
||||
status: removed
|
||||
milestone: '14.3'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.ci_templates.p_ci_templates_qualys_iac_security_wee
|
|||
description: ""
|
||||
product_section: ""
|
||||
product_stage: ""
|
||||
product_group: ""
|
||||
product_group: static_analysis
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: "14.7"
|
||||
|
|
|
|||
|
|
@ -13,4 +13,5 @@ if Rails.env.development?
|
|||
|
||||
mount LetterOpenerWeb::Engine, at: '/rails/letter_opener'
|
||||
mount Lookbook::Engine, at: '/rails/lookbook'
|
||||
mount Toogle::Engine, at: '/rails/features'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
- name: Semantic versioning in the CI/CD catalog
|
||||
description: |
|
||||
To enforce consistent behavior across published components, in GitLab 16.10 we will enforce Semantic versioning for components that are published to the CI/CD catalog. When publishing a component, the tag must follow the 3-digit semantic versioning standard (for example `1.0.0`).
|
||||
|
||||
When using a component with the `include: component` syntax, you should use the published semantic version (`x`, `x.x`. or `x.x.x`). Using `~latest` continues to be supported, but it will always return the latest published version, so you must use it with caution as it could include breaking changes. Shorthand syntax is not supported, but it will be in an upcoming milestone.
|
||||
stage: verify
|
||||
self-managed: true
|
||||
gitlab-com: true
|
||||
available_in: [Free, Premium, Ultimate]
|
||||
documentation_link: https://docs.gitlab.com/ee/ci/components/#component-versions
|
||||
image_url: https://release-16-10.about.gitlab-review.app/images/16_10/component.png
|
||||
published_at: 2024-03-21
|
||||
release: 16.10
|
||||
|
||||
- name: Offload CI traffic to Geo secondaries
|
||||
description: |
|
||||
You can now offload CI runner traffic to Geo secondary sites. Locate runner fleets where they are more convenient and economical to operate and manage, while reducing cross-region traffic. Distribute the load across multiple secondary Geo sites. Reduce load on the primary site, reserving resources for serving developer traffic. After this setup, the developer experience is transparent and seamless. Developer workflows for the setup and configuration of jobs remain unchanged.
|
||||
stage: systems
|
||||
self-managed: true
|
||||
gitlab-com: false
|
||||
available_in: [Premium, Ultimate]
|
||||
documentation_link: https://docs.gitlab.com/ee/administration/geo/secondary_proxy/runners.html
|
||||
image_url: https://release-16-10.about.gitlab-review.app/images/16_10/systems_geo_runner_acceleration.png
|
||||
published_at: 2024-03-21
|
||||
release: 16.10
|
||||
|
||||
- name: Wiki templates
|
||||
description: |
|
||||
This version of GitLab introduces all-new templates to the Wiki. Now, you can create templates to streamline creating new pages or modifying existing ones. Templates are wiki pages that are stored in the templates directory in the wiki repository.
|
||||
|
||||
With this enhancement, you can make your wiki page layouts more consistent, create or restructure pages faster, and ensure that information is presented clearly and coherently in your knowledge base.
|
||||
stage: plan
|
||||
self-managed: true
|
||||
gitlab-com: true
|
||||
available_in: [Free, Premium, Ultimate]
|
||||
documentation_link: https://docs.gitlab.com/ee/user/project/wiki/index.html#wiki-page-templates
|
||||
image_url: https://release-16-10.about.gitlab-review.app/images/16_10/wiki_templates.gif
|
||||
published_at: 2024-03-21
|
||||
release: 16.10
|
||||
|
||||
- name: New ClickHouse integration for high-performance DevOps Analytics
|
||||
description: |
|
||||
The [Contribution Analytics report](https://docs.gitlab.com/ee/user/group/contribution_analytics/) is now more performant and backed by an advanced analytics database using [ClickHouse](https://docs.gitlab.com/ee/architecture/blueprints/clickhouse_usage/#summary) on GitLab.com. This upgrade set the foundation for new extensive analytics and reporting features, allowing us to deliver high-performance analytics aggregations, filtering, and slicing across multiple dimensions. Support for self-managed customers to be able to add to this capability is proposed in [issue 441626](https://gitlab.com/gitlab-org/gitlab/-/issues/441626).
|
||||
|
||||
Although ClickHouse enhances GitLab's analytics capabilities, it's not meant to replace PostgreSQL or Redis, and the existing capabilities remain unchanged.
|
||||
stage: plan
|
||||
self-managed: false
|
||||
gitlab-com: true
|
||||
available_in: [Ultimate]
|
||||
documentation_link: https://docs.gitlab.com/ee/user/group/contribution_analytics/
|
||||
image_url: https://release-16-10.about.gitlab-review.app/images/16_10/ca_ch16.10_2.png
|
||||
published_at: 2024-03-21
|
||||
release: 16.10
|
||||
|
||||
- name: GitLab Duo access governance control
|
||||
description: |
|
||||
Generative AI is revolutionizing work processes, and you can now facilitate the adoption of these technologies without compromising privacy, compliance, or intellectual property (IP) protections.
|
||||
|
||||
You can now disable GitLab Duo AI features for a project, a group, or an instance by using the API. You can then enable GitLab Duo for specific projects or groups when you're ready. These changes are part of a suite of expected work to make AI features more granular to control.
|
||||
stage: ai-powered
|
||||
self-managed: true
|
||||
gitlab-com: true
|
||||
available_in: [Premium, Ultimate]
|
||||
documentation_link: https://docs.gitlab.com/ee/user/ai_features.html#disable-gitlab-duo-features
|
||||
image_url: https://release-16-10.about.gitlab-review.app/images/16_10/Turn-off-GitLab-Duo-features.png
|
||||
published_at: 2024-03-21
|
||||
release: 16.10
|
||||
|
||||
- name: GitLab Pages and Advanced Search available on GitLab Dedicated
|
||||
description: |
|
||||
[GitLab Pages](https://docs.gitlab.com/ee/user/project/pages/) and [Advanced Search](https://docs.gitlab.com/ee/user/search/advanced_search.html) have been enabled for all [GitLab Dedicated instances](https://about.gitlab.com/dedicated/). These features are included in your GitLab Dedicated subscription.
|
||||
|
||||
Advanced Search enables faster, more efficient search across your entire GitLab Dedicated instance. All capabilities of Advanced Search can be used with GitLab Dedicated instances.
|
||||
|
||||
With GitLab Pages, you can publish static websites directly from a repository in GitLab Dedicated. Some capabilities of Pages are [not yet available](https://docs.gitlab.com/ee/subscriptions/gitlab_dedicated/#gitlab-pages) for GitLab Dedicated instances.
|
||||
stage: platforms
|
||||
self-managed: false
|
||||
gitlab-com: true
|
||||
available_in: [Ultimate]
|
||||
documentation_link: https://docs.gitlab.com/ee/subscriptions/gitlab_dedicated/#available-features
|
||||
image_url: https://release-16-10.about.gitlab-review.app/images/16_10/gitlab-dedicated.png
|
||||
published_at: 2024-03-21
|
||||
release: 16.10
|
||||
|
|
@ -82,4 +82,4 @@ The metrics server cannot serve both HTTP and HTTPS at the same time.
|
|||
|
||||
When running [GitLab in Docker](../../../install/docker.md), your container might run out of space. This can happen if you enable certain features which increase your space consumption, for example Web Exporter.
|
||||
|
||||
To work around this issue, [update your `shm-size`](../../../install/docker.md#devshm-mount-not-having-enough-space-in-docker-container).
|
||||
To work around this issue, [update your `shm-size`](../../../install/docker_troubleshooting.md#devshm-mount-not-having-enough-space-in-docker-container).
|
||||
|
|
|
|||
|
|
@ -2676,15 +2676,57 @@ Get a single merge request diff version.
|
|||
GET /projects/:id/merge_requests/:merge_request_iid/versions/:version_id
|
||||
```
|
||||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------------|---------|----------|-------------------------------------------|
|
||||
| `id` | String | Yes | The ID of the project. |
|
||||
| `merge_request_iid` | integer | Yes | The internal ID of the merge request. |
|
||||
| `version_id` | integer | Yes | The ID of the merge request diff version. |
|
||||
| `id` | String | Yes | ID of the project. |
|
||||
| `merge_request_iid` | integer | Yes | Internal ID of the merge request. |
|
||||
| `version_id` | integer | Yes | ID of the merge request diff version. |
|
||||
| `unidiff` | boolean | No | Present diffs in the [unified diff](https://www.gnu.org/software/diffutils/manual/html_node/Detailed-Unified.html) format. Default is false. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/130610) in GitLab 16.5. |
|
||||
|
||||
For an explanation of the SHAs in the response,
|
||||
see [SHAs in the API response](#shas-in-the-api-response).
|
||||
If successful, returns [`200 OK`](rest/index.md#status-codes) and the following
|
||||
response attributes:
|
||||
|
||||
| Attribute | Type | Description |
|
||||
|-------------------------------|--------------|-------------|
|
||||
| `id` | integer | ID of the merge request diff version. |
|
||||
| `base_commit_sha` | string | Merge-base commit SHA between the source branch and the target branches. |
|
||||
| `commits` | object array | Commits in the merge request diff. |
|
||||
| `commits[].id` | string | ID of the commit. |
|
||||
| `commits[].short_id` | string | Short ID of the commit. |
|
||||
| `commits[].created_at` | datetime | Identical to the `committed_date` field. |
|
||||
| `commits[].parent_ids` | array | IDs of the parent commits. |
|
||||
| `commits[].title` | string | Commit title. |
|
||||
| `commits[].message` | string | Commit message. |
|
||||
| `commits[].author_name` | string | Commit author's name. |
|
||||
| `commits[].author_email` | string | Commit author's email address. |
|
||||
| `commits[].authored_date` | datetime | Commit authored date. |
|
||||
| `commits[].committer_name` | string | Committer's name. |
|
||||
| `commits[].committer_email` | string | Committer's email address. |
|
||||
| `commits[].committed_date` | datetime | Commit date. |
|
||||
| `commits[].trailers` | object | Git trailers that were parsed for the commit. Duplicate keys include the last value only. |
|
||||
| `commits[].extended_trailers` | object | Git trailers that were parsed for the commit. |
|
||||
| `commits[].web_url` | string | Web URL of the merge request. |
|
||||
| `created_at` | datetime | Timestamp of when the merge request was created. |
|
||||
| `diffs` | object array | Diffs in the merge request diff version. |
|
||||
| `diffs[].diff` | string | Content of the diff. |
|
||||
| `diffs[].new_path` | string | New path of the file. |
|
||||
| `diffs[].old_path` | string | Old path of the file. |
|
||||
| `diffs[].a_mode` | string | Old file mode of the file. |
|
||||
| `diffs[].b_mode` | string | New file mode of the file. |
|
||||
| `diffs[].new_file` | boolean | Indicates if the file has just been added. |
|
||||
| `diffs[].renamed_file` | boolean | Indicates if the file has been renamed. |
|
||||
| `diffs[].deleted_file` | boolean | Indicates if the file has been removed. |
|
||||
| `diffs[].generated_file` | boolean | Indicates if the file is [marked as generated](../user/project/merge_requests/changes.md#collapse-generated-files). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141576) in GitLab 16.9. |
|
||||
| `head_commit_sha` | string | HEAD commit of the source branch. |
|
||||
| `merge_request_id` | integer | ID of the merge request. |
|
||||
| `patch_id_sha` | string | [Patch ID](https://git-scm.com/docs/git-patch-id) for the merge request diff. |
|
||||
| `real_size` | string | Number of changes in the merge request diff. |
|
||||
| `start_commit_sha` | string | HEAD commit SHA of the target branch when this version of the diff was created. |
|
||||
| `state` | string | State of the merge request diff. Can be `collected`, `overflow`, `without_files`. Deprecated values: `timeout`, `overflow_commits_safe_size`, `overflow_diff_files_limit`, `overflow_diff_lines_limit`. |
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
|
|
@ -2707,27 +2749,51 @@ Example response:
|
|||
"commits": [{
|
||||
"id": "33e2ee8579fda5bc36accc9c6fbd0b4fefda9e30",
|
||||
"short_id": "33e2ee85",
|
||||
"parent_ids": [],
|
||||
"title": "Change year to 2018",
|
||||
"author_name": "Administrator",
|
||||
"author_email": "admin@example.com",
|
||||
"authored_date": "2016-07-26T17:44:29.000+03:00",
|
||||
"committer_name": "Administrator",
|
||||
"committer_email": "admin@example.com",
|
||||
"committed_date": "2016-07-26T17:44:29.000+03:00",
|
||||
"created_at": "2016-07-26T17:44:29.000+03:00",
|
||||
"message": "Change year to 2018"
|
||||
"message": "Change year to 2018",
|
||||
"trailers": {},
|
||||
"extended_trailers": {},
|
||||
"web_url": "https://gitlab.example.com/project/-/commit/33e2ee8579fda5bc36accc9c6fbd0b4fefda9e30"
|
||||
}, {
|
||||
"id": "aa24655de48b36335556ac8a3cd8bb521f977cbd",
|
||||
"short_id": "aa24655d",
|
||||
"parent_ids": [],
|
||||
"title": "Update LICENSE",
|
||||
"author_name": "Administrator",
|
||||
"author_email": "admin@example.com",
|
||||
"authored_date": "2016-07-25T17:21:53.000+03:00",
|
||||
"committer_name": "Administrator",
|
||||
"committer_email": "admin@example.com",
|
||||
"committed_date": "2016-07-25T17:21:53.000+03:00",
|
||||
"created_at": "2016-07-25T17:21:53.000+03:00",
|
||||
"message": "Update LICENSE"
|
||||
"message": "Update LICENSE",
|
||||
"trailers": {},
|
||||
"extended_trailers": {},
|
||||
"web_url": "https://gitlab.example.com/project/-/commit/aa24655de48b36335556ac8a3cd8bb521f977cbd"
|
||||
}, {
|
||||
"id": "3eed087b29835c48015768f839d76e5ea8f07a24",
|
||||
"short_id": "3eed087b",
|
||||
"parent_ids": [],
|
||||
"title": "Add license",
|
||||
"author_name": "Administrator",
|
||||
"author_email": "admin@example.com",
|
||||
"authored_date": "2016-07-25T17:21:20.000+03:00",
|
||||
"committer_name": "Administrator",
|
||||
"committer_email": "admin@example.com",
|
||||
"committed_date": "2016-07-25T17:21:20.000+03:00",
|
||||
"created_at": "2016-07-25T17:21:20.000+03:00",
|
||||
"message": "Add license"
|
||||
"message": "Add license",
|
||||
"trailers": {},
|
||||
"extended_trailers": {},
|
||||
"web_url": "https://gitlab.example.com/project/-/commit/3eed087b29835c48015768f839d76e5ea8f07a24"
|
||||
}],
|
||||
"diffs": [{
|
||||
"old_path": "LICENSE",
|
||||
|
|
@ -2737,7 +2803,8 @@ Example response:
|
|||
"diff": "@@ -0,0 +1,21 @@\n+The MIT License (MIT)\n+\n+Copyright (c) 2018 Administrator\n+\n+Permission is hereby granted, free of charge, to any person obtaining a copy\n+of this software and associated documentation files (the \"Software\"), to deal\n+in the Software without restriction, including without limitation the rights\n+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n+copies of the Software, and to permit persons to whom the Software is\n+furnished to do so, subject to the following conditions:\n+\n+The above copyright notice and this permission notice shall be included in all\n+copies or substantial portions of the Software.\n+\n+THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n+SOFTWARE.\n",
|
||||
"new_file": true,
|
||||
"renamed_file": false,
|
||||
"deleted_file": false
|
||||
"deleted_file": false,
|
||||
"generated_file": false
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -513,7 +513,7 @@ When using CI/CD variables in a component, evaluate if the `inputs` keyword
|
|||
should be used instead. Avoid asking users to define custom variables to configure
|
||||
components when `inputs` is a better solution.
|
||||
|
||||
Inputs are explicitly defined in the component's specs, and have better validation than variables.
|
||||
Inputs are explicitly defined in the component's `spec` section, and have better validation than variables.
|
||||
For example, if a required input is not passed to the component, GitLab returns a pipeline error.
|
||||
By contrast, if a variable is not defined, its value is empty, and there is no error.
|
||||
|
||||
|
|
|
|||
|
|
@ -19,9 +19,11 @@ For example, as part of a migration from [Jenkins](../migration/jenkins.md) or [
|
|||
|
||||
When authenticating with the API, you can use:
|
||||
|
||||
- A [pipeline trigger token](#create-a-pipeline-trigger-token) to trigger a branch or tag pipeline.
|
||||
- A [pipeline trigger token](#create-a-pipeline-trigger-token) to trigger a branch or tag pipeline
|
||||
with the [pipeline triggers API endpoint](../../api/pipeline_triggers.md).
|
||||
- A [CI/CD job token](../jobs/ci_job_token.md) to [trigger a multi-project pipeline](../pipelines/downstream_pipelines.md#trigger-a-multi-project-pipeline-by-using-the-api).
|
||||
- A [personal access token](../../user/profile/personal_access_tokens.md).
|
||||
- Another [token with API access](../../security/token_overview.md) to create a new pipeline
|
||||
with the [project pipeline API endpoint](../../api/pipelines.md#create-a-new-pipeline).
|
||||
|
||||
## Create a pipeline trigger token
|
||||
|
||||
|
|
@ -57,7 +59,7 @@ pipelines with a tool that can access the API, or a webhook.
|
|||
|
||||
### Use cURL
|
||||
|
||||
You can use cURL to trigger pipelines with the [pipeline trigger token API endpoint](../../api/pipeline_triggers.md).
|
||||
You can use cURL to trigger pipelines with the [pipeline triggers API endpoint](../../api/pipeline_triggers.md).
|
||||
For example:
|
||||
|
||||
- Use a multiline cURL command:
|
||||
|
|
|
|||
|
|
@ -824,7 +824,7 @@ Use the interpolation format `$[[ input.input-id ]]` to reference the values out
|
|||
Inputs are evaluated and interpolated when the configuration is fetched during pipeline creation, but before the
|
||||
configuration is merged with the contents of the `.gitlab-ci.yml` file.
|
||||
|
||||
**Keyword type**: Header keyword. `specs` must be declared at the top of the configuration file,
|
||||
**Keyword type**: Header keyword. `spec` must be declared at the top of the configuration file,
|
||||
in a header section.
|
||||
|
||||
**Possible inputs**: A hash of strings representing the expected inputs.
|
||||
|
|
@ -864,7 +864,7 @@ Inputs are mandatory when included, unless you set a default value with `spec:in
|
|||
|
||||
Use `default: ''` to have no default value.
|
||||
|
||||
**Keyword type**: Header keyword. `specs` must be declared at the top of the configuration file,
|
||||
**Keyword type**: Header keyword. `spec` must be declared at the top of the configuration file,
|
||||
in a header section.
|
||||
|
||||
**Possible inputs**: A string representing the default value, or `''`.
|
||||
|
|
@ -906,7 +906,7 @@ Use `description` to give a description to a specific input. The description doe
|
|||
not affect the behavior of the input and is only used to help users of the file
|
||||
understand the input.
|
||||
|
||||
**Keyword type**: Header keyword. `specs` must be declared at the top of the configuration file,
|
||||
**Keyword type**: Header keyword. `spec` must be declared at the top of the configuration file,
|
||||
in a header section.
|
||||
|
||||
**Possible inputs**: A string representing the description.
|
||||
|
|
@ -930,7 +930,7 @@ spec:
|
|||
Inputs can use `options` to specify a list of allowed values for an input.
|
||||
The limit is 50 options per input.
|
||||
|
||||
**Keyword type**: Header keyword. `specs` must be declared at the top of the configuration file,
|
||||
**Keyword type**: Header keyword. `spec` must be declared at the top of the configuration file,
|
||||
in a header section.
|
||||
|
||||
**Possible inputs**: An array of input options.
|
||||
|
|
@ -968,7 +968,7 @@ In this example:
|
|||
|
||||
Use `spec:inputs:regex` to specify a regular expression that the input must match.
|
||||
|
||||
**Keyword type**: Header keyword. `specs` must be declared at the top of the configuration file,
|
||||
**Keyword type**: Header keyword. `spec` must be declared at the top of the configuration file,
|
||||
in a header section.
|
||||
|
||||
**Possible inputs**: Must be a regular expression that starts and ends with the `/` character.
|
||||
|
|
@ -998,7 +998,7 @@ An input of `v1.A.B` does not match the regular expression and fails validation.
|
|||
By default, inputs expect strings. Use `spec:inputs:type` to set a different required
|
||||
type for inputs.
|
||||
|
||||
**Keyword type**: Header keyword. `specs` must be declared at the top of the configuration file,
|
||||
**Keyword type**: Header keyword. `spec` must be declared at the top of the configuration file,
|
||||
in a header section.
|
||||
|
||||
**Possible inputs**: Can be one of:
|
||||
|
|
@ -4348,20 +4348,20 @@ build-prod:
|
|||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
script: echo "Default branch, so building prod version..."
|
||||
|
||||
specs:
|
||||
tests:
|
||||
stage: test
|
||||
needs: ['build-dev']
|
||||
rules:
|
||||
- if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
|
||||
needs: ['build-prod']
|
||||
- when: on_success # Run the job in other cases
|
||||
script: echo "Running dev specs by default, or prod specs when default branch..."
|
||||
script: echo "Running tests for dev by default, or prod when default branch..."
|
||||
```
|
||||
|
||||
In this example:
|
||||
|
||||
- If the pipeline runs on a branch that is not the default branch, the `specs` job needs the `build-dev` job (default behavior).
|
||||
- If the pipeline runs on the default branch, and therefore the rule matches the condition, the `specs` job needs the `build-prod` job instead.
|
||||
- If the pipeline runs on a branch that is not the default branch, the `tests` job needs the `build-dev` job (default behavior).
|
||||
- If the pipeline runs on the default branch, and therefore the rule matches the condition, the `tests` job needs the `build-prod` job instead.
|
||||
|
||||
**Additional details**:
|
||||
|
||||
|
|
|
|||
|
|
@ -133,73 +133,25 @@ For information on testing migrations, review our
|
|||
NOTE:
|
||||
You must have `AllFeaturesUser` [`psql` access](#access-database-lab-engine) to access the console with `psql`.
|
||||
|
||||
#### Simplified access through `pgai` Ruby gem
|
||||
To access the database lab instances, you must:
|
||||
|
||||
[@mbobin](https://gitlab.com/mbobin) created the [`pgai` Ruby Gem](https://gitlab.com/mbobin/pgai/#pgai) that
|
||||
greatly simplifies access to a database clone, with support for:
|
||||
- File an [access request](https://handbook.gitlab.com/handbook/business-technology/end-user-services/onboarding-access-requests/access-requests/#individual-or-bulk-access-request).
|
||||
- Have a user data bag entry in [chef-repo](https://gitlab.com/gitlab-com/gl-infra/chef-repo) with your SSH key and the `db-lab` role.
|
||||
- Configure `ssh` as follows:
|
||||
|
||||
- Access to all database clones listed in the [Postgres.ai instances page](https://console.postgres.ai/gitlab/instances);
|
||||
- Multiple `psql` sessions on the same clone.
|
||||
```plaintext
|
||||
Host lb-bastion.db-lab.gitlab.com
|
||||
User ${USER}
|
||||
IdentitiesOnly yes
|
||||
IdentityFile ~/.ssh/ed25519
|
||||
|
||||
If you have `AllFeaturesUser` [`psql` access](#access-database-lab-engine), you can follow the steps below to configure
|
||||
the `pgai` Gem:
|
||||
|
||||
1. To get started, you need to gather some values from the [Postgres.ai instances page](https://console.postgres.ai/gitlab/instances):
|
||||
|
||||
1. Go to the instance that you want to configure and the on right side of the screen.
|
||||
1. Under **Connection**, select **Connect**. The menu might be collapsed.
|
||||
|
||||
A dialog with everything that's needed for configuration appears, using this format:
|
||||
|
||||
```shell
|
||||
dblab init --url "http://127.0.0.1:1234" --token TOKEN --environment-id <environment-id>
|
||||
```
|
||||
|
||||
```shell
|
||||
ssh -NTML 1234:localhost:<environment-port> <postgresai-user>@<postgresai-proxy> -i ~/.ssh/id_rsa
|
||||
```
|
||||
|
||||
1. Add the following snippet to your SSH configuration file at `~/.ssh/config`, replacing the variable values:
|
||||
|
||||
```plaintext
|
||||
Host pgai-proxy
|
||||
HostName <postgresai-proxy>
|
||||
User <postgresai-user>
|
||||
IdentityFile ~/.ssh/id_ed25519
|
||||
```
|
||||
|
||||
1. Run the following command so you can accept the server key fingerprint:
|
||||
|
||||
```shell
|
||||
ssh pgai-proxy
|
||||
```
|
||||
|
||||
1. Run the following commands:
|
||||
|
||||
```shell
|
||||
gem install pgai
|
||||
|
||||
# Grab an access token: https://console.postgres.ai/gitlab/tokens
|
||||
# GITLAB_USER is your GitLab handle
|
||||
pgai config --dbname=gitlabhq_dblab --prefix=$GITLAB_USER --proxy=pgai-proxy
|
||||
|
||||
# Grab the respective port values from https://console.postgres.ai/gitlab/instances
|
||||
# for the instances you'll be using (in this case, for the `main` database instance)
|
||||
pgai env add --alias main --id <environment-id> --port <environment-port>
|
||||
```
|
||||
|
||||
1. Once this one-time configuration is done, you can use `pgai connect` to connect to a particular database. For
|
||||
instance, to connect to the `main` database:
|
||||
|
||||
```shell
|
||||
pgai connect main
|
||||
```
|
||||
|
||||
1. Once done with the clone, you can destroy it:
|
||||
|
||||
```shell
|
||||
pgai destroy main
|
||||
```
|
||||
Host *.gitlab-db-lab.internal
|
||||
User ${USER}
|
||||
PreferredAuthentications publickey
|
||||
IdentitiesOnly yes
|
||||
IdentityFile ~/.ssh/ed25519
|
||||
ProxyCommand ssh lb-bastion.db-lab.gitlab.com -W %h:%p
|
||||
```
|
||||
|
||||
#### Manual access through the Postgres.ai instances page
|
||||
|
||||
|
|
@ -211,7 +163,7 @@ To connect to a clone using `psql`:
|
|||
1. Create a clone from the [desired instance](https://console.postgres.ai/gitlab/instances/).
|
||||
1. Provide a **Clone ID**: Something that uniquely identifies your clone, such as `yourname-testing-gitlabissue`.
|
||||
1. Provide a **Database username** and **Database password**: Connects `psql` to your clone.
|
||||
1. Select **Enable deletion protection** if you want to preserve your clone. Avoid selecting this option.
|
||||
1. Select **Enable deletion protection** if you need to preserve your clone. Avoid selecting this option.
|
||||
Clones are removed after 12 hours.
|
||||
1. In the **Clone details** page of the Postgres.ai web interface, copy and run
|
||||
the command to start SSH port forwarding for the clone.
|
||||
|
|
@ -220,3 +172,10 @@ To connect to a clone using `psql`:
|
|||
|
||||
After you connect, use clone like you would any `psql` console in production, but with
|
||||
the added benefit and safety of an isolated writeable environment.
|
||||
|
||||
#### Simplified access through `pgai` Ruby gem
|
||||
|
||||
WARNING:
|
||||
The `pgai` gem has not yet been updated to use the new database lab instances so you will only be able to access the legacy instance using this tool.
|
||||
|
||||
For instructions on using the `pgai` Ruby gem, see: [Database Lab access using the pgai Ruby gem](database_lab_pgai.md).
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
---
|
||||
stage: Data Stores
|
||||
group: Database
|
||||
info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
|
||||
---
|
||||
|
||||
# Database Lab access using the `pgai` Ruby gem
|
||||
|
||||
WARNING:
|
||||
The `pgai` gem has not yet been updated to use the new database lab instances so you will only be able to access `gitlab-production-main` and `gitlab-production-ci` using this tool.
|
||||
|
||||
[@mbobin](https://gitlab.com/mbobin) created the [`pgai` Ruby Gem](https://gitlab.com/mbobin/pgai/#pgai) that
|
||||
greatly simplifies access to a database clone, with support for:
|
||||
|
||||
- Access to all database clones listed in the [Postgres.ai instances page](https://console.postgres.ai/gitlab/instances);
|
||||
- Multiple `psql` sessions on the same clone.
|
||||
|
||||
If you have `AllFeaturesUser` [`psql` access](database_lab.md#access-database-lab-engine),
|
||||
you can follow the steps below to configure the `pgai` Gem:
|
||||
|
||||
1. To get started, you need to gather some values from the [Postgres.ai instances page](https://console.postgres.ai/gitlab/instances):
|
||||
|
||||
1. Go to the instance that you want to configure and the on right side of the screen.
|
||||
1. Under **Connection**, select **Connect**. The menu might be collapsed.
|
||||
|
||||
A dialog with everything that's needed for configuration appears, using this format:
|
||||
|
||||
```shell
|
||||
dblab init --url "http://127.0.0.1:1234" --token TOKEN --environment-id <environment-id>
|
||||
```
|
||||
|
||||
```shell
|
||||
ssh -NTML 1234:localhost:<environment-port> <postgresai-user>@<postgresai-proxy> -i ~/.ssh/id_rsa
|
||||
```
|
||||
|
||||
1. Add the following snippet to your SSH configuration file at `~/.ssh/config`, replacing the variable values:
|
||||
|
||||
```plaintext
|
||||
Host pgai-proxy
|
||||
HostName <postgresai-proxy>
|
||||
User <postgresai-user>
|
||||
IdentityFile ~/.ssh/id_ed25519
|
||||
```
|
||||
|
||||
1. Run the following command so you can accept the server key fingerprint:
|
||||
|
||||
```shell
|
||||
ssh pgai-proxy
|
||||
```
|
||||
|
||||
1. Run the following commands:
|
||||
|
||||
```shell
|
||||
gem install pgai
|
||||
|
||||
# Grab an access token: https://console.postgres.ai/gitlab/tokens
|
||||
# GITLAB_USER is your GitLab handle
|
||||
pgai config --dbname=gitlabhq_dblab --prefix=$GITLAB_USER --proxy=pgai-proxy
|
||||
|
||||
# Grab the respective port values from https://console.postgres.ai/gitlab/instances
|
||||
# for the instances you'll be using (in this case, for the `main` database instance)
|
||||
pgai env add --alias main --id <environment-id> --port <environment-port>
|
||||
```
|
||||
|
||||
1. Once this one-time configuration is done, you can use `pgai connect` to connect to a particular database. For
|
||||
instance, to connect to the `main` database:
|
||||
|
||||
```shell
|
||||
pgai connect main
|
||||
```
|
||||
|
||||
1. Once done with the clone, you can destroy it:
|
||||
|
||||
```shell
|
||||
pgai destroy main
|
||||
```
|
||||
|
|
@ -4,7 +4,7 @@ group: Distribution
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Install GitLab using Docker
|
||||
# Install GitLab by using Docker
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Free, Premium, Ultimate
|
||||
|
|
@ -700,150 +700,3 @@ docker exec -t <container name> gitlab-backup create SKIP=artifacts,repositories
|
|||
|
||||
The backup is written to `/var/opt/gitlab/backups` which should be on a
|
||||
[volume mounted by Docker](#set-up-the-volumes-location).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
The following information will help if you encounter problems with an installation that used the Linux package and Docker.
|
||||
|
||||
### Diagnose potential problems
|
||||
|
||||
Read container logs:
|
||||
|
||||
```shell
|
||||
sudo docker logs gitlab
|
||||
```
|
||||
|
||||
Enter running container:
|
||||
|
||||
```shell
|
||||
sudo docker exec -it gitlab /bin/bash
|
||||
```
|
||||
|
||||
From within the container you can administer the GitLab container as you would
|
||||
usually administer a [Linux package installation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md).
|
||||
|
||||
### 500 Internal Error
|
||||
|
||||
When updating the Docker image you may encounter an issue where all paths
|
||||
display a `500` page. If this occurs, restart the container to try to rectify the
|
||||
issue:
|
||||
|
||||
```shell
|
||||
sudo docker restart gitlab
|
||||
```
|
||||
|
||||
### Permission problems
|
||||
|
||||
When updating from older GitLab Docker images you might encounter permission
|
||||
problems. This happens when users in previous images were not
|
||||
preserved correctly. There's script that fixes permissions for all files.
|
||||
|
||||
To fix your container, execute `update-permissions` and restart the
|
||||
container afterwards:
|
||||
|
||||
```shell
|
||||
sudo docker exec gitlab update-permissions
|
||||
sudo docker restart gitlab
|
||||
```
|
||||
|
||||
### Windows/Mac: `Error executing action run on resource ruby_block[directory resource: /data/GitLab]`
|
||||
|
||||
This error occurs when using Docker Toolbox with VirtualBox on Windows or Mac,
|
||||
and making use of Docker volumes. The `/c/Users` volume is mounted as a
|
||||
VirtualBox Shared Folder, and does not support the all POSIX file system features.
|
||||
The directory ownership and permissions cannot be changed without remounting, and
|
||||
GitLab fails.
|
||||
|
||||
Our recommendation is to switch to using the native Docker install for your
|
||||
platform, instead of using Docker Toolbox.
|
||||
|
||||
If you cannot use the native Docker install (Windows 10 Home Edition, or Windows 7/8),
|
||||
then an alternative solution is to setup NFS mounts instead of VirtualBox shares for
|
||||
Docker Toolbox's boot2docker.
|
||||
|
||||
### Linux ACL issues
|
||||
|
||||
If you are using file ACLs on the Docker host, the `docker` group requires full access to the volumes in order for GitLab to work:
|
||||
|
||||
```shell
|
||||
getfacl $GITLAB_HOME
|
||||
|
||||
# file: $GITLAB_HOME
|
||||
# owner: XXXX
|
||||
# group: XXXX
|
||||
user::rwx
|
||||
group::rwx
|
||||
group:docker:rwx
|
||||
mask::rwx
|
||||
default:user::rwx
|
||||
default:group::rwx
|
||||
default:group:docker:rwx
|
||||
default:mask::rwx
|
||||
default:other::r-x
|
||||
```
|
||||
|
||||
If these are not correct, set them with:
|
||||
|
||||
```shell
|
||||
sudo setfacl -mR default:group:docker:rwx $GITLAB_HOME
|
||||
```
|
||||
|
||||
The default group is `docker`. If you changed the group, be sure to update your
|
||||
commands.
|
||||
|
||||
### `/dev/shm` mount not having enough space in Docker container
|
||||
|
||||
GitLab comes with a Prometheus metrics endpoint at `/-/metrics` to expose a
|
||||
variety of statistics on the health and performance of GitLab. The files
|
||||
required for this gets written to a temporary file system (like `/run` or
|
||||
`/dev/shm`).
|
||||
|
||||
By default, Docker allocates 64 MB to the shared memory directory (mounted at
|
||||
`/dev/shm`). This is insufficient to hold all the Prometheus metrics related
|
||||
files generated, and will generate error logs like the following:
|
||||
|
||||
```plaintext
|
||||
writing value to /dev/shm/gitlab/sidekiq/gauge_all_sidekiq_0-1.db failed with unmapped file
|
||||
writing value to /dev/shm/gitlab/sidekiq/gauge_all_sidekiq_0-1.db failed with unmapped file
|
||||
writing value to /dev/shm/gitlab/sidekiq/gauge_all_sidekiq_0-1.db failed with unmapped file
|
||||
writing value to /dev/shm/gitlab/sidekiq/histogram_sidekiq_0-0.db failed with unmapped file
|
||||
writing value to /dev/shm/gitlab/sidekiq/histogram_sidekiq_0-0.db failed with unmapped file
|
||||
writing value to /dev/shm/gitlab/sidekiq/histogram_sidekiq_0-0.db failed with unmapped file
|
||||
writing value to /dev/shm/gitlab/sidekiq/histogram_sidekiq_0-0.db failed with unmapped file
|
||||
```
|
||||
|
||||
Other than disabling the Prometheus Metrics from the Admin Area, the recommended
|
||||
solution to fix this problem is to increase the size of shared memory to at least 256 MB.
|
||||
If using `docker run`, this can be done by passing the flag `--shm-size 256m`.
|
||||
If using a `docker-compose.yml` file, the `shm_size` key can be used for this
|
||||
purpose.
|
||||
|
||||
### Docker containers exhausts space due to the `json-file`
|
||||
|
||||
Docker uses the [`json-file` default logging driver](https://docs.docker.com/config/containers/logging/configure/#configure-the-default-logging-driver), which performs no log rotation by default. As a result of this lack of rotation, log files stored by the `json-file` driver can consume a significant amount of disk space for containers that generate a lot of output. This can lead to disk space exhaustion. To address this, use [`journald`](https://docs.docker.com/config/containers/logging/journald/) as the logging driver when available, or [another supported driver](https://docs.docker.com/config/containers/logging/configure/#supported-logging-drivers) with native rotation support.
|
||||
|
||||
### Buffer overflow error when starting Docker
|
||||
|
||||
If you receive this buffer overflow error, you should purge old log files in
|
||||
`/var/log/gitlab`:
|
||||
|
||||
```plaintext
|
||||
buffer overflow detected : terminated
|
||||
xargs: tail: terminated by signal 6
|
||||
```
|
||||
|
||||
Removing old log files helps fix the error, and ensures a clean startup of the instance.
|
||||
|
||||
### ThreadError can't create Thread Operation not permitted
|
||||
|
||||
```plaintext
|
||||
can't create Thread: Operation not permitted
|
||||
```
|
||||
|
||||
This error occurs when running a container built with newer `glibc` versions on a
|
||||
[host that doesn't have support for the new clone3 function](https://github.com/moby/moby/issues/42680). In GitLab 16.0 and later, the container image includes
|
||||
the Ubuntu 22.04 Linux package which is built with this newer `glibc`.
|
||||
|
||||
This problem is fixed with newer container runtime tools like [Docker 20.10.10](https://github.com/moby/moby/pull/42836).
|
||||
|
||||
To resolve this issue, update Docker to version 20.10.10 or later.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,156 @@
|
|||
---
|
||||
stage: Systems
|
||||
group: Distribution
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Troubleshooting GitLab installations that use Docker
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Free, Premium, Ultimate
|
||||
**Offering:** Self-managed
|
||||
|
||||
When installing GitLab by using Docker, you might encounter the following issues.
|
||||
|
||||
## Diagnose potential problems
|
||||
|
||||
Read container logs:
|
||||
|
||||
```shell
|
||||
sudo docker logs gitlab
|
||||
```
|
||||
|
||||
Enter running container:
|
||||
|
||||
```shell
|
||||
sudo docker exec -it gitlab /bin/bash
|
||||
```
|
||||
|
||||
From within the container you can administer the GitLab container as you would
|
||||
usually administer a [Linux package installation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md).
|
||||
|
||||
## 500 Internal Error
|
||||
|
||||
When updating the Docker image you may encounter an issue where all paths
|
||||
display a `500` page. If this occurs, restart the container to try to rectify the
|
||||
issue:
|
||||
|
||||
```shell
|
||||
sudo docker restart gitlab
|
||||
```
|
||||
|
||||
## Permission problems
|
||||
|
||||
When updating from older GitLab Docker images you might encounter permission
|
||||
problems. This happens when users in previous images were not
|
||||
preserved correctly. There's script that fixes permissions for all files.
|
||||
|
||||
To fix your container, execute `update-permissions` and restart the
|
||||
container afterwards:
|
||||
|
||||
```shell
|
||||
sudo docker exec gitlab update-permissions
|
||||
sudo docker restart gitlab
|
||||
```
|
||||
|
||||
## Windows/Mac: `Error executing action run on resource ruby_block[directory resource: /data/GitLab]`
|
||||
|
||||
This error occurs when using Docker Toolbox with VirtualBox on Windows or Mac,
|
||||
and making use of Docker volumes. The `/c/Users` volume is mounted as a
|
||||
VirtualBox Shared Folder, and does not support the all POSIX file system features.
|
||||
The directory ownership and permissions cannot be changed without remounting, and
|
||||
GitLab fails.
|
||||
|
||||
Our recommendation is to switch to using the native Docker install for your
|
||||
platform, instead of using Docker Toolbox.
|
||||
|
||||
If you cannot use the native Docker install (Windows 10 Home Edition, or Windows 7/8),
|
||||
then an alternative solution is to setup NFS mounts instead of VirtualBox shares for
|
||||
Docker Toolbox's boot2docker.
|
||||
|
||||
## Linux ACL issues
|
||||
|
||||
If you are using file ACLs on the Docker host, the `docker` group requires full access to the volumes in order for GitLab to work:
|
||||
|
||||
```shell
|
||||
getfacl $GITLAB_HOME
|
||||
|
||||
# file: $GITLAB_HOME
|
||||
# owner: XXXX
|
||||
# group: XXXX
|
||||
user::rwx
|
||||
group::rwx
|
||||
group:docker:rwx
|
||||
mask::rwx
|
||||
default:user::rwx
|
||||
default:group::rwx
|
||||
default:group:docker:rwx
|
||||
default:mask::rwx
|
||||
default:other::r-x
|
||||
```
|
||||
|
||||
If these are not correct, set them with:
|
||||
|
||||
```shell
|
||||
sudo setfacl -mR default:group:docker:rwx $GITLAB_HOME
|
||||
```
|
||||
|
||||
The default group is `docker`. If you changed the group, be sure to update your
|
||||
commands.
|
||||
|
||||
## `/dev/shm` mount not having enough space in Docker container
|
||||
|
||||
GitLab comes with a Prometheus metrics endpoint at `/-/metrics` to expose a
|
||||
variety of statistics on the health and performance of GitLab. The files
|
||||
required for this gets written to a temporary file system (like `/run` or
|
||||
`/dev/shm`).
|
||||
|
||||
By default, Docker allocates 64 MB to the shared memory directory (mounted at
|
||||
`/dev/shm`). This is insufficient to hold all the Prometheus metrics related
|
||||
files generated, and will generate error logs like the following:
|
||||
|
||||
```plaintext
|
||||
writing value to /dev/shm/gitlab/sidekiq/gauge_all_sidekiq_0-1.db failed with unmapped file
|
||||
writing value to /dev/shm/gitlab/sidekiq/gauge_all_sidekiq_0-1.db failed with unmapped file
|
||||
writing value to /dev/shm/gitlab/sidekiq/gauge_all_sidekiq_0-1.db failed with unmapped file
|
||||
writing value to /dev/shm/gitlab/sidekiq/histogram_sidekiq_0-0.db failed with unmapped file
|
||||
writing value to /dev/shm/gitlab/sidekiq/histogram_sidekiq_0-0.db failed with unmapped file
|
||||
writing value to /dev/shm/gitlab/sidekiq/histogram_sidekiq_0-0.db failed with unmapped file
|
||||
writing value to /dev/shm/gitlab/sidekiq/histogram_sidekiq_0-0.db failed with unmapped file
|
||||
```
|
||||
|
||||
Other than disabling the Prometheus Metrics from the Admin Area, the recommended
|
||||
solution to fix this problem is to increase the size of shared memory to at least 256 MB.
|
||||
If using `docker run`, this can be done by passing the flag `--shm-size 256m`.
|
||||
If using a `docker-compose.yml` file, the `shm_size` key can be used for this
|
||||
purpose.
|
||||
|
||||
## Docker containers exhausts space due to the `json-file`
|
||||
|
||||
Docker uses the [`json-file` default logging driver](https://docs.docker.com/config/containers/logging/configure/#configure-the-default-logging-driver), which performs no log rotation by default. As a result of this lack of rotation, log files stored by the `json-file` driver can consume a significant amount of disk space for containers that generate a lot of output. This can lead to disk space exhaustion. To address this, use [`journald`](https://docs.docker.com/config/containers/logging/journald/) as the logging driver when available, or [another supported driver](https://docs.docker.com/config/containers/logging/configure/#supported-logging-drivers) with native rotation support.
|
||||
|
||||
## Buffer overflow error when starting Docker
|
||||
|
||||
If you receive this buffer overflow error, you should purge old log files in
|
||||
`/var/log/gitlab`:
|
||||
|
||||
```plaintext
|
||||
buffer overflow detected : terminated
|
||||
xargs: tail: terminated by signal 6
|
||||
```
|
||||
|
||||
Removing old log files helps fix the error, and ensures a clean startup of the instance.
|
||||
|
||||
## ThreadError can't create Thread Operation not permitted
|
||||
|
||||
```plaintext
|
||||
can't create Thread: Operation not permitted
|
||||
```
|
||||
|
||||
This error occurs when running a container built with newer `glibc` versions on a
|
||||
[host that doesn't have support for the new clone3 function](https://github.com/moby/moby/issues/42680). In GitLab 16.0 and later, the container image includes
|
||||
the Ubuntu 22.04 Linux package which is built with this newer `glibc`.
|
||||
|
||||
This problem is fixed with newer container runtime tools like [Docker 20.10.10](https://github.com/moby/moby/pull/42836).
|
||||
|
||||
To resolve this issue, update Docker to version 20.10.10 or later.
|
||||
|
|
@ -376,122 +376,3 @@ When you create a keytab with Advanced Encryption Standard (AES)-only encryption
|
|||
1. Right-click the account and select **Properties**.
|
||||
1. In **Account Options** on the **Account** tab, select the appropriate AES encryption support checkbox.
|
||||
1. Save and close.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Using Google Chrome with Kerberos authentication against Windows AD
|
||||
|
||||
When you use Google Chrome to sign in to GitLab with Kerberos, you must enter your full username. For example, `username@domain.com`.
|
||||
|
||||
If you do not enter your full username, the sign-in fails. Check the logs to see the following event message as evidence of this sign-in failure:
|
||||
|
||||
```plain
|
||||
"message":"OmniauthKerberosController: failed to process Negotiate/Kerberos authentication: gss_accept_sec_context did not return GSS_S_COMPLETE: An unsupported mechanism was requested\nUnknown error"`.
|
||||
```
|
||||
|
||||
### Test connectivity between the GitLab and Kerberos servers
|
||||
|
||||
You can use utilities like [`kinit`](https://web.mit.edu/kerberos/krb5-1.12/doc/user/user_commands/kinit.html) and [`klist`](https://web.mit.edu/kerberos/krb5-1.12/doc/user/user_commands/klist.html) to test connectivity between the GitLab server
|
||||
and the Kerberos server. How you install these depends on your specific OS.
|
||||
|
||||
Use `klist` to see the service principal names (SPN) available in your `keytab` file and the encryption type for each SPN:
|
||||
|
||||
```shell
|
||||
klist -ke /etc/http.keytab
|
||||
```
|
||||
|
||||
On an Ubuntu server, the output would look similar to the following:
|
||||
|
||||
```shell
|
||||
Keytab name: FILE:/etc/http.keytab
|
||||
KVNO Principal
|
||||
---- --------------------------------------------------------------------------
|
||||
3 HTTP/my.gitlab.domain@MY.REALM (des-cbc-crc)
|
||||
3 HTTP/my.gitlab.domain@MY.REALM (des-cbc-md5)
|
||||
3 HTTP/my.gitlab.domain@MY.REALM (arcfour-hmac)
|
||||
3 HTTP/my.gitlab.domain@MY.REALM (aes256-cts-hmac-sha1-96)
|
||||
3 HTTP/my.gitlab.domain@MY.REALM (aes128-cts-hmac-sha1-96)
|
||||
```
|
||||
|
||||
Use `kinit` in verbose mode to test whether GitLab can use the keytab file to connect to the Kerberos server:
|
||||
|
||||
```shell
|
||||
KRB5_TRACE=/dev/stdout kinit -kt /etc/http.keytab HTTP/my.gitlab.domain@MY.REALM
|
||||
```
|
||||
|
||||
This command shows a detailed output of the authentication process.
|
||||
|
||||
### Unsupported GSSAPI mechanism
|
||||
|
||||
With Kerberos SPNEGO authentication, the browser is expected to send a list of
|
||||
mechanisms it supports to GitLab. If it doesn't support any of the mechanisms
|
||||
GitLab supports, authentication fails with a message like this in the log:
|
||||
|
||||
```plaintext
|
||||
OmniauthKerberosController: failed to process Negotiate/Kerberos authentication: gss_accept_sec_context did not return GSS_S_COMPLETE: An unsupported mechanism was requested Unknown error
|
||||
```
|
||||
|
||||
There are a number of potential causes and solutions for this error message.
|
||||
|
||||
#### Kerberos integration not using a dedicated port
|
||||
|
||||
GitLab CI/CD doesn't work with a Kerberos-enabled GitLab instance unless the Kerberos integration
|
||||
is configured to [use a dedicated port](kerberos.md#http-git-access-with-kerberos-token-passwordless-authentication).
|
||||
|
||||
#### Lack of connectivity between client machine and Kerberos server
|
||||
|
||||
This is usually seen when the browser is unable to contact the Kerberos server
|
||||
directly. It falls back to an unsupported mechanism known as
|
||||
[`IAKERB`](https://k5wiki.kerberos.org/wiki/Projects/IAKERB), which tries to use
|
||||
the GitLab server as an intermediary to the Kerberos server.
|
||||
|
||||
If you're experiencing this error, ensure there is connectivity between the
|
||||
client machine and the Kerberos server - this is a prerequisite! Traffic may be
|
||||
blocked by a firewall, or the DNS records may be incorrect.
|
||||
|
||||
#### `GitLab DNS record is a CNAME record` error
|
||||
|
||||
Kerberos fails with this error when GitLab is referenced with a `CNAME` record.
|
||||
To resolve this issue, ensure the DNS record for GitLab is an `A` record.
|
||||
|
||||
#### Mismatched forward and reverse DNS records for GitLab instance hostname
|
||||
|
||||
Another failure mode occurs when the forward and reverse DNS records for the
|
||||
GitLab server do not match. Often, Windows clients work in this case while
|
||||
Linux clients fail. They use reverse DNS while detecting the Kerberos
|
||||
realm. If they get the wrong realm then ordinary Kerberos mechanisms fail,
|
||||
so the client falls back to attempting to negotiate `IAKERB`, leading to the
|
||||
above error message.
|
||||
|
||||
To fix this, ensure that the forward and reverse DNS for your GitLab server
|
||||
match. So for instance, if you access GitLab as `gitlab.example.com`, resolving
|
||||
to IP address `10.0.2.2`, then `2.2.0.10.in-addr.arpa` must be a `PTR` record for
|
||||
`gitlab.example.com`.
|
||||
|
||||
#### Missing Kerberos libraries on browser or client machine
|
||||
|
||||
Finally, it's possible that the browser or client machine lack Kerberos support
|
||||
completely. Ensure that the Kerberos libraries are installed and that you can
|
||||
authenticate to other Kerberos services.
|
||||
|
||||
### HTTP Basic: Access denied when cloning
|
||||
|
||||
```shell
|
||||
remote: HTTP Basic: Access denied
|
||||
fatal: Authentication failed for '<KRB5 path>'
|
||||
```
|
||||
|
||||
If you are using Git v2.11 or newer and see the above error when cloning, you can
|
||||
set the `http.emptyAuth` Git option to `true` to fix this:
|
||||
|
||||
```shell
|
||||
git config --global http.emptyAuth true
|
||||
```
|
||||
|
||||
See also: [Git v2.11 release notes](https://github.com/git/git/blob/master/Documentation/RelNotes/2.11.0.txt#L482-L486)
|
||||
|
||||
## Helpful links
|
||||
|
||||
- <https://help.ubuntu.com/community/Kerberos>
|
||||
- <https://blog.manula.org/2012/04/setting-up-kerberos-server-with-debian.html>
|
||||
- <https://www.roguelynn.com/words/explain-like-im-5-kerberos/>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,130 @@
|
|||
---
|
||||
stage: Govern
|
||||
group: Authentication
|
||||
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments"
|
||||
---
|
||||
|
||||
# Troubleshooting GitLab with Kerberos integration
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Free, Premium, Ultimate
|
||||
**Offering:** Self-managed
|
||||
|
||||
When working with GitLab with Kerberos integration, you might encounter the following issues.
|
||||
|
||||
## Using Google Chrome with Kerberos authentication against Windows AD
|
||||
|
||||
When you use Google Chrome to sign in to GitLab with Kerberos, you must enter your full username. For example, `username@domain.com`.
|
||||
|
||||
If you do not enter your full username, the sign-in fails. Check the logs to see the following event message as evidence of this sign-in failure:
|
||||
|
||||
```plain
|
||||
"message":"OmniauthKerberosController: failed to process Negotiate/Kerberos authentication: gss_accept_sec_context did not return GSS_S_COMPLETE: An unsupported mechanism was requested\nUnknown error"`.
|
||||
```
|
||||
|
||||
## Test connectivity between the GitLab and Kerberos servers
|
||||
|
||||
You can use utilities like [`kinit`](https://web.mit.edu/kerberos/krb5-1.12/doc/user/user_commands/kinit.html) and [`klist`](https://web.mit.edu/kerberos/krb5-1.12/doc/user/user_commands/klist.html) to test connectivity between the GitLab server
|
||||
and the Kerberos server. How you install these depends on your specific OS.
|
||||
|
||||
Use `klist` to see the service principal names (SPN) available in your `keytab` file and the encryption type for each SPN:
|
||||
|
||||
```shell
|
||||
klist -ke /etc/http.keytab
|
||||
```
|
||||
|
||||
On an Ubuntu server, the output would look similar to the following:
|
||||
|
||||
```shell
|
||||
Keytab name: FILE:/etc/http.keytab
|
||||
KVNO Principal
|
||||
---- --------------------------------------------------------------------------
|
||||
3 HTTP/my.gitlab.domain@MY.REALM (des-cbc-crc)
|
||||
3 HTTP/my.gitlab.domain@MY.REALM (des-cbc-md5)
|
||||
3 HTTP/my.gitlab.domain@MY.REALM (arcfour-hmac)
|
||||
3 HTTP/my.gitlab.domain@MY.REALM (aes256-cts-hmac-sha1-96)
|
||||
3 HTTP/my.gitlab.domain@MY.REALM (aes128-cts-hmac-sha1-96)
|
||||
```
|
||||
|
||||
Use `kinit` in verbose mode to test whether GitLab can use the keytab file to connect to the Kerberos server:
|
||||
|
||||
```shell
|
||||
KRB5_TRACE=/dev/stdout kinit -kt /etc/http.keytab HTTP/my.gitlab.domain@MY.REALM
|
||||
```
|
||||
|
||||
This command shows a detailed output of the authentication process.
|
||||
|
||||
## Unsupported GSSAPI mechanism
|
||||
|
||||
With Kerberos SPNEGO authentication, the browser is expected to send a list of
|
||||
mechanisms it supports to GitLab. If it doesn't support any of the mechanisms
|
||||
GitLab supports, authentication fails with a message like this in the log:
|
||||
|
||||
```plaintext
|
||||
OmniauthKerberosController: failed to process Negotiate/Kerberos authentication: gss_accept_sec_context did not return GSS_S_COMPLETE: An unsupported mechanism was requested Unknown error
|
||||
```
|
||||
|
||||
There are a number of potential causes and solutions for this error message.
|
||||
|
||||
### Kerberos integration not using a dedicated port
|
||||
|
||||
GitLab CI/CD doesn't work with a Kerberos-enabled GitLab instance unless the Kerberos integration
|
||||
is configured to [use a dedicated port](kerberos.md#http-git-access-with-kerberos-token-passwordless-authentication).
|
||||
|
||||
### Lack of connectivity between client machine and Kerberos server
|
||||
|
||||
This is usually seen when the browser is unable to contact the Kerberos server
|
||||
directly. It falls back to an unsupported mechanism known as
|
||||
[`IAKERB`](https://k5wiki.kerberos.org/wiki/Projects/IAKERB), which tries to use
|
||||
the GitLab server as an intermediary to the Kerberos server.
|
||||
|
||||
If you're experiencing this error, ensure there is connectivity between the
|
||||
client machine and the Kerberos server - this is a prerequisite! Traffic may be
|
||||
blocked by a firewall, or the DNS records may be incorrect.
|
||||
|
||||
### `GitLab DNS record is a CNAME record` error
|
||||
|
||||
Kerberos fails with this error when GitLab is referenced with a `CNAME` record.
|
||||
To resolve this issue, ensure the DNS record for GitLab is an `A` record.
|
||||
|
||||
### Mismatched forward and reverse DNS records for GitLab instance hostname
|
||||
|
||||
Another failure mode occurs when the forward and reverse DNS records for the
|
||||
GitLab server do not match. Often, Windows clients work in this case while
|
||||
Linux clients fail. They use reverse DNS while detecting the Kerberos
|
||||
realm. If they get the wrong realm then ordinary Kerberos mechanisms fail,
|
||||
so the client falls back to attempting to negotiate `IAKERB`, leading to the
|
||||
above error message.
|
||||
|
||||
To fix this, ensure that the forward and reverse DNS for your GitLab server
|
||||
match. So for instance, if you access GitLab as `gitlab.example.com`, resolving
|
||||
to IP address `10.0.2.2`, then `2.2.0.10.in-addr.arpa` must be a `PTR` record for
|
||||
`gitlab.example.com`.
|
||||
|
||||
### Missing Kerberos libraries on browser or client machine
|
||||
|
||||
Finally, it's possible that the browser or client machine lack Kerberos support
|
||||
completely. Ensure that the Kerberos libraries are installed and that you can
|
||||
authenticate to other Kerberos services.
|
||||
|
||||
## HTTP Basic: Access denied when cloning
|
||||
|
||||
```shell
|
||||
remote: HTTP Basic: Access denied
|
||||
fatal: Authentication failed for '<KRB5 path>'
|
||||
```
|
||||
|
||||
If you are using Git v2.11 or newer and see the above error when cloning, you can
|
||||
set the `http.emptyAuth` Git option to `true` to fix this:
|
||||
|
||||
```shell
|
||||
git config --global http.emptyAuth true
|
||||
```
|
||||
|
||||
See also: [Git v2.11 release notes](https://github.com/git/git/blob/master/Documentation/RelNotes/2.11.0.txt#L482-L486)
|
||||
|
||||
## Helpful links
|
||||
|
||||
- <https://help.ubuntu.com/community/Kerberos>
|
||||
- <https://blog.manula.org/2012/04/setting-up-kerberos-server-with-debian.html>
|
||||
- <https://www.roguelynn.com/words/explain-like-im-5-kerberos/>
|
||||
|
|
@ -952,7 +952,7 @@ by this issue.
|
|||
every Sidekiq process also listens to those queues to ensure all jobs are processed across
|
||||
all queues. This behavior does not apply if you have configured the [routing rules](../../administration/sidekiq/processing_specific_job_classes.md#routing-rules).
|
||||
- Docker 20.10.10 or later is required to run the GitLab Docker image. Older versions
|
||||
[throw errors on startup](../../install/docker.md#threaderror-cant-create-thread-operation-not-permitted).
|
||||
[throw errors on startup](../../install/docker_troubleshooting.md#threaderror-cant-create-thread-operation-not-permitted).
|
||||
- Container registry using Azure storage might be empty with zero tags. You can fix this by following the [breaking change instructions](../deprecations.md#azure-storage-driver-defaults-to-the-correct-root-prefix).
|
||||
|
||||
- Normally, backups in environments that have PgBouncer must [bypass PgBouncer by setting variables that are prefixed with `GITLAB_BACKUP_`](../../administration/backup_restore/backup_gitlab.md#bypassing-pgbouncer). However, due to an [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/422163), `gitlab-backup` uses the regular database connection through PgBouncer instead of the direct connection defined in the override, and the database backup fails. The workaround is to use `pg_dump` directly.
|
||||
|
|
|
|||
|
|
@ -79,9 +79,10 @@ module API
|
|||
import_export_upload: ImportExportUpload.new(import_file: params[:file])
|
||||
}
|
||||
|
||||
group = ::Groups::CreateService.new(current_user, group_params).execute
|
||||
response = ::Groups::CreateService.new(current_user, group_params).execute
|
||||
group = response[:group]
|
||||
|
||||
if group.persisted?
|
||||
if response.success?
|
||||
::Groups::ImportExport::ImportService.new(group: group, user: current_user).async_execute
|
||||
|
||||
accepted!
|
||||
|
|
|
|||
|
|
@ -62,9 +62,7 @@ module API
|
|||
def create_group
|
||||
# This is a separate method so that EE can extend its behaviour, without
|
||||
# having to modify this code directly.
|
||||
::Groups::CreateService
|
||||
.new(current_user, declared_params(include_missing: false))
|
||||
.execute
|
||||
::Groups::CreateService.new(current_user, declared_params(include_missing: false)).execute
|
||||
end
|
||||
|
||||
def update_group(group)
|
||||
|
|
@ -244,10 +242,11 @@ module API
|
|||
authorize_group_creation!
|
||||
end
|
||||
|
||||
group = create_group
|
||||
response = create_group
|
||||
group = response[:group]
|
||||
group.preload_shared_group_links
|
||||
|
||||
if group.persisted?
|
||||
if response.success?
|
||||
present group, with: Entities::GroupDetail, current_user: current_user
|
||||
else
|
||||
render_api_error!("Failed to save group #{group.errors.messages}", 400)
|
||||
|
|
|
|||
|
|
@ -22,9 +22,10 @@ module BulkImports
|
|||
raise(GroupCreationError, 'User requires Two-Factor Authentication')
|
||||
end
|
||||
|
||||
group = ::Groups::CreateService.new(current_user, data).execute
|
||||
response = ::Groups::CreateService.new(current_user, data).execute
|
||||
group = response[:group]
|
||||
|
||||
raise(GroupCreationError, group.errors.full_messages.to_sentence) if group.errors.any?
|
||||
raise(GroupCreationError, group.errors.full_messages.to_sentence) if response.error?
|
||||
|
||||
context.entity.update!(group: group)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,14 +19,6 @@ module Gitlab
|
|||
|
||||
attr_reader :data, :ctx, :errors
|
||||
|
||||
def self.match(data)
|
||||
return data unless data.is_a?(String) && data.include?(PREFIX)
|
||||
|
||||
data.gsub(PATTERN) do
|
||||
yield ::Regexp.last_match(1), ::Regexp.last_match(2)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(block, data, ctx)
|
||||
@block = block
|
||||
@data = data
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue