Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
53ab147992
commit
06c57a8378
6
Gemfile
6
Gemfile
|
@ -320,9 +320,7 @@ gem 'premailer-rails', '~> 1.10.3'
|
|||
|
||||
# LabKit: Tracing and Correlation
|
||||
gem 'gitlab-labkit', '~> 0.24.0'
|
||||
# Thrift is a dependency of gitlab-labkit, we want a version higher than 0.14.0
|
||||
# because of https://gitlab.com/gitlab-org/gitlab/-/issues/321900
|
||||
gem 'thrift', '>= 0.14.0'
|
||||
gem 'thrift', '>= 0.16.0'
|
||||
|
||||
# I18n
|
||||
gem 'ruby_parser', '~> 3.15', require: false
|
||||
|
@ -408,7 +406,7 @@ group :development, :test do
|
|||
end
|
||||
|
||||
group :development, :test, :danger do
|
||||
gem 'gitlab-dangerfiles', '~> 3.5.0', require: false
|
||||
gem 'gitlab-dangerfiles', '~> 3.5.1', require: false
|
||||
end
|
||||
|
||||
group :development, :test, :coverage do
|
||||
|
|
|
@ -516,7 +516,7 @@ GEM
|
|||
terminal-table (~> 1.5, >= 1.5.1)
|
||||
gitlab-chronic (0.10.5)
|
||||
numerizer (~> 0.2)
|
||||
gitlab-dangerfiles (3.5.0)
|
||||
gitlab-dangerfiles (3.5.1)
|
||||
danger (>= 8.4.5)
|
||||
danger-gitlab (>= 8.0.0)
|
||||
rake
|
||||
|
@ -1364,7 +1364,7 @@ GEM
|
|||
faraday (~> 1.0)
|
||||
text (1.3.1)
|
||||
thor (1.2.1)
|
||||
thrift (0.14.0)
|
||||
thrift (0.16.0)
|
||||
tilt (2.0.10)
|
||||
timecop (0.9.1)
|
||||
timeliness (0.3.10)
|
||||
|
@ -1577,7 +1577,7 @@ DEPENDENCIES
|
|||
gitaly (~> 15.3.0.pre.rc4)
|
||||
github-markup (~> 1.7.0)
|
||||
gitlab-chronic (~> 0.10.5)
|
||||
gitlab-dangerfiles (~> 3.5.0)
|
||||
gitlab-dangerfiles (~> 3.5.1)
|
||||
gitlab-experiment (~> 0.7.1)
|
||||
gitlab-fog-azure-rm (~> 1.3.0)
|
||||
gitlab-labkit (~> 0.24.0)
|
||||
|
@ -1759,7 +1759,7 @@ DEPENDENCIES
|
|||
terser (= 1.0.2)
|
||||
test-prof (~> 1.0.7)
|
||||
test_file_finder (~> 0.1.3)
|
||||
thrift (>= 0.14.0)
|
||||
thrift (>= 0.16.0)
|
||||
timecop (~> 0.9.1)
|
||||
timfel-krb5-auth (~> 0.8)
|
||||
toml-rb (~> 2.0)
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
function addBlameLink(containerSelector, linkClass) {
|
||||
const containerEl = document.querySelector(containerSelector);
|
||||
|
||||
if (!containerEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
containerEl.addEventListener('mouseover', (e) => {
|
||||
const isLineLink = e.target.classList.contains(linkClass);
|
||||
if (isLineLink) {
|
||||
const lineLink = e.target;
|
||||
const lineLinkCopy = lineLink.cloneNode(true);
|
||||
lineLinkCopy.classList.remove(linkClass, 'diff-line-num');
|
||||
|
||||
const { lineNumber } = lineLink.dataset;
|
||||
const { blamePath } = document.querySelector('.line-numbers').dataset;
|
||||
const blameLink = document.createElement('a');
|
||||
blameLink.classList.add('file-line-blame');
|
||||
blameLink.href = `${blamePath}#L${lineNumber}`;
|
||||
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.classList.add('line-links', 'diff-line-num');
|
||||
|
||||
wrapper.appendChild(blameLink);
|
||||
wrapper.appendChild(lineLinkCopy);
|
||||
lineLink.replaceWith(wrapper);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default addBlameLink;
|
|
@ -1,7 +1,12 @@
|
|||
import Tracking from '~/tracking';
|
||||
|
||||
function addBlobLinksTracking(containerSelector, eventsToTrack) {
|
||||
const containerEl = document.querySelector(containerSelector);
|
||||
const eventsToTrack = [
|
||||
{ selector: '.file-line-blame', property: 'blame' },
|
||||
{ selector: '.file-line-num', property: 'link' },
|
||||
];
|
||||
|
||||
function addBlobLinksTracking() {
|
||||
const containerEl = document.querySelector('.file-holder');
|
||||
|
||||
if (!containerEl) {
|
||||
return;
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<script>
|
||||
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
import { mapActions, mapGetters, mapState } from 'vuex';
|
||||
import { s__ } from '~/locale';
|
||||
import { DEFAULT_BOARD_LIST_ITEMS_SIZE } from 'ee_else_ce/boards/constants';
|
||||
|
||||
import Tracking from '~/tracking';
|
||||
|
||||
|
@ -34,6 +33,7 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['pageInfoByListId']),
|
||||
...mapGetters(['getBoardItemsByList']),
|
||||
tracking() {
|
||||
return {
|
||||
|
@ -45,6 +45,9 @@ export default {
|
|||
listItems() {
|
||||
return this.getBoardItemsByList(this.list.id);
|
||||
},
|
||||
listHasNextPage() {
|
||||
return this.pageInfoByListId[this.list.id]?.hasNextPage;
|
||||
},
|
||||
firstItemInListId() {
|
||||
return this.listItems[0]?.id;
|
||||
},
|
||||
|
@ -58,7 +61,7 @@ export default {
|
|||
return `${this.item.id}-${this.item.iid}-${this.index}`;
|
||||
},
|
||||
showMoveToEndOfList() {
|
||||
return this.lengthOfListItemsInBoard <= DEFAULT_BOARD_LIST_ITEMS_SIZE;
|
||||
return !this.listHasNextPage;
|
||||
},
|
||||
isFirstItemInList() {
|
||||
return this.index === 0;
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
<script>
|
||||
import { BubbleMenuPlugin } from '@tiptap/extension-bubble-menu';
|
||||
|
||||
export default {
|
||||
name: 'BubbleMenu',
|
||||
inject: ['tiptapEditor'],
|
||||
props: {
|
||||
pluginKey: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
shouldShow: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
tippyOptions: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
menuVisible: false,
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
await this.$nextTick();
|
||||
|
||||
this.tiptapEditor.registerPlugin(
|
||||
BubbleMenuPlugin({
|
||||
pluginKey: this.pluginKey,
|
||||
editor: this.tiptapEditor,
|
||||
element: this.$el,
|
||||
shouldShow: this.shouldShow,
|
||||
tippyOptions: {
|
||||
...this.tippyOptions,
|
||||
onShow: (...args) => {
|
||||
this.$emit('show', ...args);
|
||||
this.menuVisible = true;
|
||||
},
|
||||
onHidden: (...args) => {
|
||||
this.$emit('hidden', ...args);
|
||||
this.menuVisible = false;
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.tiptapEditor.unregisterPlugin(this.pluginKey);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<slot v-if="menuVisible"></slot>
|
||||
</div>
|
||||
</template>
|
|
@ -10,13 +10,13 @@ import {
|
|||
GlSearchBoxByType,
|
||||
GlTooltipDirective as GlTooltip,
|
||||
} from '@gitlab/ui';
|
||||
import { BubbleMenu } from '@tiptap/vue-2';
|
||||
import { getParentByTagName } from '~/lib/utils/dom_utils';
|
||||
import codeBlockLanguageLoader from '../../services/code_block_language_loader';
|
||||
import CodeBlockHighlight from '../../extensions/code_block_highlight';
|
||||
import Diagram from '../../extensions/diagram';
|
||||
import Frontmatter from '../../extensions/frontmatter';
|
||||
import EditorStateObserver from '../editor_state_observer.vue';
|
||||
import BubbleMenu from './bubble_menu.vue';
|
||||
|
||||
const CODE_BLOCK_NODE_TYPES = [CodeBlockHighlight.name, Diagram.name, Frontmatter.name];
|
||||
|
||||
|
@ -129,6 +129,10 @@ export default {
|
|||
deleteCodeBlock() {
|
||||
this.tiptapEditor.chain().focus().deleteNode(this.codeBlockType).run();
|
||||
},
|
||||
|
||||
tippyOptions() {
|
||||
return { getReferenceClientRect: this.getReferenceClientRect.bind(this) };
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -136,12 +140,9 @@ export default {
|
|||
<bubble-menu
|
||||
data-testid="code-block-bubble-menu"
|
||||
class="gl-shadow gl-rounded-base gl-bg-white"
|
||||
:editor="tiptapEditor"
|
||||
plugin-key="bubbleMenuCodeBlock"
|
||||
:should-show="shouldShow"
|
||||
:tippy-options="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
|
||||
getReferenceClientRect,
|
||||
} /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
|
||||
:tippy-options="tippyOptions()"
|
||||
>
|
||||
<editor-state-observer @transaction="updateCodeBlockInfoToState">
|
||||
<gl-button-group>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<script>
|
||||
import { GlButtonGroup } from '@gitlab/ui';
|
||||
import { BubbleMenu } from '@tiptap/vue-2';
|
||||
import { BUBBLE_MENU_TRACKING_ACTION } from '../../constants';
|
||||
import trackUIControl from '../../services/track_ui_control';
|
||||
import Paragraph from '../../extensions/paragraph';
|
||||
|
@ -9,6 +8,7 @@ import Audio from '../../extensions/audio';
|
|||
import Video from '../../extensions/video';
|
||||
import Image from '../../extensions/image';
|
||||
import ToolbarButton from '../toolbar_button.vue';
|
||||
import BubbleMenu from './bubble_menu.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -34,14 +34,17 @@ export default {
|
|||
);
|
||||
},
|
||||
},
|
||||
toggleLinkCommandParams: {
|
||||
href: '',
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<bubble-menu
|
||||
data-testid="formatting-bubble-menu"
|
||||
class="gl-shadow gl-rounded-base gl-bg-white"
|
||||
:editor="tiptapEditor"
|
||||
:should-show="shouldShow"
|
||||
:plugin-key="'formatting'"
|
||||
>
|
||||
<gl-button-group>
|
||||
<toolbar-button
|
||||
|
@ -109,9 +112,7 @@ export default {
|
|||
content-type="link"
|
||||
icon-name="link"
|
||||
editor-command="toggleLink"
|
||||
:editor-command-params="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
|
||||
href: '',
|
||||
} /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
|
||||
:editor-command-params="$options.toggleLinkCommandParams"
|
||||
category="tertiary"
|
||||
size="medium"
|
||||
:label="__('Insert link')"
|
||||
|
|
|
@ -8,9 +8,9 @@ import {
|
|||
GlButtonGroup,
|
||||
GlTooltipDirective as GlTooltip,
|
||||
} from '@gitlab/ui';
|
||||
import { BubbleMenu } from '@tiptap/vue-2';
|
||||
import Link from '../../extensions/link';
|
||||
import EditorStateObserver from '../editor_state_observer.vue';
|
||||
import BubbleMenu from './bubble_menu.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -109,18 +109,18 @@ export default {
|
|||
this.tiptapEditor.chain().focus().extendMarkRange(Link.name).unsetLink().run();
|
||||
},
|
||||
},
|
||||
tippyOptions: {
|
||||
placement: 'bottom',
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<bubble-menu
|
||||
data-testid="link-bubble-menu"
|
||||
class="gl-shadow gl-rounded-base gl-bg-white"
|
||||
:editor="tiptapEditor"
|
||||
plugin-key="bubbleMenuLink"
|
||||
:should-show="() => shouldShow()"
|
||||
:tippy-options="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
|
||||
placement: 'bottom',
|
||||
} /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
|
||||
:should-show="shouldShow"
|
||||
:tippy-options="$options.tippyOptions"
|
||||
>
|
||||
<editor-state-observer @transaction="updateLinkToState">
|
||||
<gl-button-group v-if="!isEditing" class="gl-display-flex gl-align-items-center">
|
||||
|
|
|
@ -9,13 +9,13 @@ import {
|
|||
GlButtonGroup,
|
||||
GlTooltipDirective as GlTooltip,
|
||||
} from '@gitlab/ui';
|
||||
import { BubbleMenu } from '@tiptap/vue-2';
|
||||
import { __ } from '~/locale';
|
||||
import Audio from '../../extensions/audio';
|
||||
import Image from '../../extensions/image';
|
||||
import Video from '../../extensions/video';
|
||||
import EditorStateObserver from '../editor_state_observer.vue';
|
||||
import { acceptedMimes } from '../../services/upload_helpers';
|
||||
import BubbleMenu from './bubble_menu.vue';
|
||||
|
||||
const MEDIA_TYPES = [Audio.name, Image.name, Video.name];
|
||||
|
||||
|
@ -189,9 +189,8 @@ export default {
|
|||
<bubble-menu
|
||||
data-testid="media-bubble-menu"
|
||||
class="gl-shadow gl-rounded-base gl-bg-white"
|
||||
:editor="tiptapEditor"
|
||||
plugin-key="bubbleMenuMedia"
|
||||
:should-show="() => shouldShow()"
|
||||
:should-show="shouldShow"
|
||||
>
|
||||
<editor-state-observer @transaction="updateMediaInfoToState">
|
||||
<gl-button-group v-if="!isEditing" class="gl-display-flex gl-align-items-center">
|
||||
|
|
|
@ -4,7 +4,6 @@ import BlobForkSuggestion from '~/blob/blob_fork_suggestion';
|
|||
import BlobLinePermalinkUpdater from '~/blob/blob_line_permalink_updater';
|
||||
import LineHighlighter from '~/blob/line_highlighter';
|
||||
import initBlobBundle from '~/blob_edit/blob_bundle';
|
||||
import addBlobLinksTracking from '~/blob/blob_links_tracking';
|
||||
|
||||
export default () => {
|
||||
new LineHighlighter(); // eslint-disable-line no-new
|
||||
|
@ -16,12 +15,6 @@ export default () => {
|
|||
document.querySelectorAll('.js-data-file-blob-permalink-url, .js-blob-blame-link'),
|
||||
);
|
||||
|
||||
const eventsToTrack = [
|
||||
{ selector: '.file-line-blame', property: 'blame' },
|
||||
{ selector: '.file-line-num', property: 'link' },
|
||||
];
|
||||
addBlobLinksTracking('#blob-content-holder', eventsToTrack);
|
||||
|
||||
const fileBlobPermalinkUrlElement = document.querySelector('.js-data-file-blob-permalink-url');
|
||||
const fileBlobPermalinkUrl =
|
||||
fileBlobPermalinkUrlElement && fileBlobPermalinkUrlElement.getAttribute('href');
|
||||
|
|
|
@ -13,6 +13,7 @@ import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
|||
import WebIdeLink from '~/vue_shared/components/web_ide_link.vue';
|
||||
import CodeIntelligence from '~/code_navigation/components/app.vue';
|
||||
import LineHighlighter from '~/blob/line_highlighter';
|
||||
import addBlameLink from '~/blob/blob_blame_link';
|
||||
import getRefMixin from '../mixins/get_ref';
|
||||
import blobInfoQuery from '../queries/blob_info.query.graphql';
|
||||
import userInfoQuery from '../queries/user_info.query.graphql';
|
||||
|
@ -242,6 +243,7 @@ export default {
|
|||
|
||||
if (type === SIMPLE_BLOB_VIEWER) {
|
||||
new LineHighlighter(); // eslint-disable-line no-new
|
||||
addBlameLink('.file-holder', 'js-line-links');
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -344,6 +344,7 @@ export default {
|
|||
>
|
||||
<gl-link
|
||||
v-gl-tooltip="tooltipText"
|
||||
class="gl-reset-color gl-hover-text-blue-800"
|
||||
:href="attributeUrl"
|
||||
:data-qa-selector="`${formatIssuableAttribute.snake}_link`"
|
||||
>
|
||||
|
|
|
@ -91,6 +91,12 @@ export default {
|
|||
fastForwardMergeText() {
|
||||
return __('Merge blocked: the source branch must be rebased onto the target branch.');
|
||||
},
|
||||
showRebaseWithoutPipeline() {
|
||||
return (
|
||||
!this.mr.onlyAllowMergeIfPipelineSucceeds ||
|
||||
(this.mr.onlyAllowMergeIfPipelineSucceeds && this.mr.allowMergeOnSkippedPipeline)
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
rebase({ skipCi = false } = {}) {
|
||||
|
@ -192,6 +198,7 @@ export default {
|
|||
</template>
|
||||
<template v-if="!isLoading" #actions>
|
||||
<gl-button
|
||||
v-if="showRebaseWithoutPipeline"
|
||||
:loading="isMakingRequest"
|
||||
variant="confirm"
|
||||
size="small"
|
||||
|
|
|
@ -3,6 +3,7 @@ query getState($projectPath: ID!, $iid: String!) {
|
|||
id
|
||||
archived
|
||||
onlyAllowMergeIfPipelineSucceeds
|
||||
allowMergeOnSkippedPipeline
|
||||
mergeRequest(iid: $iid) {
|
||||
id
|
||||
autoMergeEnabled
|
||||
|
|
|
@ -168,6 +168,7 @@ export default class MergeRequestStore {
|
|||
this.mergeError = data.merge_error;
|
||||
this.mergeStatus = data.merge_status;
|
||||
this.onlyAllowMergeIfPipelineSucceeds = data.only_allow_merge_if_pipeline_succeeds || false;
|
||||
this.allowMergeOnSkippedPipeline = data.allow_merge_on_skipped_pipeline || false;
|
||||
this.projectArchived = data.project_archived;
|
||||
this.isSHAMismatch = this.sha !== data.diff_head_sha;
|
||||
this.shouldBeRebased = Boolean(data.should_be_rebased);
|
||||
|
@ -195,6 +196,7 @@ export default class MergeRequestStore {
|
|||
|
||||
this.projectArchived = project.archived;
|
||||
this.onlyAllowMergeIfPipelineSucceeds = project.onlyAllowMergeIfPipelineSucceeds;
|
||||
this.allowMergeOnSkippedPipeline = project.allowMergeOnSkippedPipeline;
|
||||
|
||||
this.autoMergeEnabled = mergeRequest.autoMergeEnabled;
|
||||
this.canBeMerged = mergeRequest.mergeStatus === 'can_be_merged';
|
||||
|
|
|
@ -3,6 +3,7 @@ import { GlSafeHtmlDirective, GlLoadingIcon } from '@gitlab/ui';
|
|||
import LineHighlighter from '~/blob/line_highlighter';
|
||||
import eventHub from '~/notes/event_hub';
|
||||
import languageLoader from '~/content_editor/services/highlight_js_language_loader';
|
||||
import addBlobLinksTracking from '~/blob/blob_links_tracking';
|
||||
import Tracking from '~/tracking';
|
||||
import {
|
||||
EVENT_ACTION,
|
||||
|
@ -66,6 +67,7 @@ export default {
|
|||
},
|
||||
},
|
||||
async created() {
|
||||
addBlobLinksTracking();
|
||||
this.trackEvent(EVENT_LABEL_VIEWER);
|
||||
|
||||
if (this.unsupportedLanguage) {
|
||||
|
|
|
@ -5,16 +5,14 @@ import { helpPagePath } from '~/helpers/help_page_helper';
|
|||
|
||||
export default {
|
||||
i18n: {
|
||||
learnTasksButtonText: s__('WorkItem|Learn about tasks'),
|
||||
workItemsText: s__('WorkItem|work items'),
|
||||
learnTasksLinkText: s__('WorkItem|Learn about tasks.'),
|
||||
tasksInformationTitle: s__('WorkItem|Introducing tasks'),
|
||||
tasksInformationBody: s__(
|
||||
'WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon.',
|
||||
'WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}',
|
||||
),
|
||||
},
|
||||
helpPageLinks: {
|
||||
tasksDocLinkPath: helpPagePath('user/tasks'),
|
||||
workItemsLinkPath: helpPagePath(`development/work_items`),
|
||||
},
|
||||
components: {
|
||||
GlAlert,
|
||||
|
@ -38,16 +36,14 @@ export default {
|
|||
v-if="showInfoBanner"
|
||||
variant="tip"
|
||||
:title="$options.i18n.tasksInformationTitle"
|
||||
:primary-button-link="$options.helpPageLinks.tasksDocLinkPath"
|
||||
:primary-button-text="$options.i18n.learnTasksButtonText"
|
||||
data-testid="work-item-information"
|
||||
class="gl-mt-3"
|
||||
@dismiss="$emit('work-item-banner-dismissed')"
|
||||
>
|
||||
<gl-sprintf :message="$options.i18n.tasksInformationBody">
|
||||
<template #workItemsLink>
|
||||
<gl-link :href="$options.helpPageLinks.workItemsLinkPath">{{
|
||||
$options.i18n.workItemsText
|
||||
<template #learnMoreLink>
|
||||
<gl-link :href="$options.helpPageLinks.tasksDocLinkPath">{{
|
||||
$options.i18n.learnTasksLinkText
|
||||
}}</gl-link>
|
||||
</template>
|
||||
></gl-sprintf
|
||||
|
|
|
@ -95,23 +95,14 @@ td.line-numbers {
|
|||
|
||||
.blob-viewer {
|
||||
.line-numbers {
|
||||
min-width: 6rem;
|
||||
// for server-side-rendering
|
||||
.line-links {
|
||||
@include gl-display-flex;
|
||||
|
||||
|
||||
&:first-child {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
// for client
|
||||
&.line-links {
|
||||
min-width: 6rem;
|
||||
border-bottom-left-radius: 0;
|
||||
|
||||
+ pre {
|
||||
|
@ -120,15 +111,15 @@ td.line-numbers {
|
|||
}
|
||||
}
|
||||
|
||||
.line-links {
|
||||
&:hover a::before,
|
||||
&:focus-within a::before {
|
||||
@include gl-visibility-visible;
|
||||
}
|
||||
.line-numbers:not(.line-links) a:hover::before,
|
||||
.line-numbers:not(.line-links) a:focus-within::before,
|
||||
.line-links:hover a::before,
|
||||
.line-links:focus-within a::before {
|
||||
@include gl-visibility-visible;
|
||||
}
|
||||
|
||||
|
||||
.file-line-num {
|
||||
min-width: 4.5rem;
|
||||
@include gl-justify-content-end;
|
||||
@include gl-flex-grow-1;
|
||||
@include gl-pr-3;
|
||||
|
|
|
@ -28,7 +28,7 @@ class Projects::IncidentsController < Projects::ApplicationController
|
|||
.inc_relations_for_view
|
||||
.iid_in(params[:id])
|
||||
.without_order
|
||||
.first
|
||||
.take # rubocop:disable CodeReuse/ActiveRecord
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -17,14 +17,15 @@ module SearchArguments
|
|||
|
||||
def ready?(**args)
|
||||
validate_search_in_params!(args)
|
||||
validate_anonymous_search_access!
|
||||
validate_anonymous_search_access!(args)
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_anonymous_search_access!
|
||||
def validate_anonymous_search_access!(args)
|
||||
return unless args[:search].present?
|
||||
return if current_user.present? || Feature.disabled?(:disable_anonymous_search, type: :ops)
|
||||
|
||||
raise ::Gitlab::Graphql::Errors::ArgumentError,
|
||||
|
|
|
@ -36,10 +36,5 @@ module AlertManagement
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
override :resolving_alert?
|
||||
def resolving_alert?
|
||||
incoming_payload.resolved?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -113,7 +113,7 @@ module AlertManagement
|
|||
end
|
||||
|
||||
def resolving_alert?
|
||||
incoming_payload.ends_at.present?
|
||||
incoming_payload.resolved?
|
||||
end
|
||||
|
||||
def notifying_alert?
|
||||
|
@ -121,7 +121,7 @@ module AlertManagement
|
|||
end
|
||||
|
||||
def alert_source
|
||||
incoming_payload.monitoring_tool
|
||||
incoming_payload.source
|
||||
end
|
||||
|
||||
def logger
|
||||
|
|
|
@ -35,11 +35,6 @@ module Projects
|
|||
Gitlab::Utils::DeepSize.new(params).valid?
|
||||
end
|
||||
|
||||
override :alert_source
|
||||
def alert_source
|
||||
super || integration&.name || 'Generic Alert Endpoint'
|
||||
end
|
||||
|
||||
def active_integration?
|
||||
integration&.active?
|
||||
end
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
#blob-content.file-content.code.js-syntax-highlight
|
||||
- offset = defined?(first_line_number) ? first_line_number : 1
|
||||
.line-numbers{ class: "gl-p-0\!" }
|
||||
- blame_path = project_blame_path(@project, tree_join(@ref, blob.path))
|
||||
.line-numbers{ class: "gl-px-0!", data: { blame_path: blame_path } }
|
||||
- if blob.data.present?
|
||||
- link = blob_link if defined?(blob_link)
|
||||
- blame_link = project_blame_path(@project, tree_join(@ref, blob.path))
|
||||
- blob.data.each_line.each_with_index do |_, index|
|
||||
- i = index + offset
|
||||
-# We're not using `link_to` because it is too slow once we get to thousands of lines.
|
||||
.line-links.diff-line-num
|
||||
- if Feature.enabled?(:file_line_blame)
|
||||
%a.file-line-blame{ href: "#{blame_link}#L#{i}" }
|
||||
%a.file-line-num{ href: "#{link}#L#{i}", id: "L#{i}", 'data-line-number' => i }
|
||||
= i
|
||||
%a.file-line-num.diff-line-num{ class: ("js-line-links" if Feature.enabled?(:file_line_blame)), href: "#{link}#L#{i}", id: "L#{i}", 'data-line-number' => i }
|
||||
= i
|
||||
- highlight = defined?(highlight_line) && highlight_line ? highlight_line - offset : nil
|
||||
.blob-content{ data: { blob_id: blob.id, path: blob.path, highlight_line: highlight, qa_selector: 'file_content' } }
|
||||
%pre.code.highlight
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
- name: "SaaS certificate-based integration with Kubernetes"
|
||||
announcement_milestone: "14.5"
|
||||
announcement_date: "2021-11-15"
|
||||
removal_milestone: "15.6"
|
||||
removal_date: "2022-11-22" # the date of the milestone release when this feature is planned to be removed
|
||||
removal_milestone: "15.9"
|
||||
removal_date: "2023-02-22" # the date of the milestone release when this feature is planned to be removed
|
||||
breaking_change: true
|
||||
body: |
|
||||
The certificate-based integration with Kubernetes will be [deprecated and removed](https://about.gitlab.com/blog/2021/11/15/deprecating-the-cert-based-kubernetes-integration/). As a GitLab SaaS customer, on new namespaces, you will no longer be able to integrate GitLab and your cluster using the certificate-based approach as of GitLab 15.0. The integration for current users will be enabled per namespace. The integrations are expected to be switched off completely on GitLab SaaS around 2022 November 22.
|
||||
|
|
|
@ -31,7 +31,7 @@ Configure your load balancers to pass connections on port 443 as 'TCP' rather
|
|||
than 'HTTP(S)' protocol. This passes the connection to the application nodes
|
||||
NGINX service untouched. NGINX has the SSL certificate and listen on port 443.
|
||||
|
||||
See [NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
|
||||
See the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
|
||||
for details on managing SSL certificates and configuring NGINX.
|
||||
|
||||
### Load Balancers terminate SSL without backend SSL
|
||||
|
@ -41,8 +41,8 @@ The load balancers is be responsible for managing SSL certificates and
|
|||
terminating SSL.
|
||||
|
||||
Because communication between the load balancers and GitLab isn't secure,
|
||||
there is some additional configuration needed. See
|
||||
[NGINX Proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#supporting-proxied-ssl)
|
||||
there is some additional configuration needed. See the
|
||||
[proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-a-reverse-proxy-or-load-balancer-ssl-termination)
|
||||
for details.
|
||||
|
||||
### Load Balancers terminate SSL with backend SSL
|
||||
|
@ -55,7 +55,7 @@ Traffic is secure between the load balancers and NGINX in this
|
|||
scenario. There is no need to add configuration for proxied SSL because the
|
||||
connection is secure all the way. However, configuration must be
|
||||
added to GitLab to configure SSL certificates. See
|
||||
[NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
|
||||
the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
|
||||
for details on managing SSL certificates and configuring NGINX.
|
||||
|
||||
## Ports
|
||||
|
@ -128,5 +128,5 @@ The default ciphers for a GitLab version can be
|
|||
viewed in the [`files/gitlab-cookbooks/gitlab/attributes/default.rb`](https://gitlab.com/gitlab-org/omnibus-gitlab/-/blob/master/files/gitlab-cookbooks/gitlab/attributes/default.rb)
|
||||
file and selecting the Git tag that correlates with your target GitLab version
|
||||
(for example `15.0.5+ee.0`). If required by your load balancer, you can then define
|
||||
[custom SSL ciphers](https://docs.gitlab.com/omnibus/settings/nginx.html#using-custom-ssl-ciphers)
|
||||
[custom SSL ciphers](https://docs.gitlab.com/omnibus/settings/ssl.html#use-custom-ssl-ciphers)
|
||||
for NGINX.
|
||||
|
|
|
@ -23,7 +23,7 @@ may or may not be available by default.
|
|||
|
||||
The Container Registry is automatically enabled and available on your GitLab domain, port 5050 if:
|
||||
|
||||
- You're using the built-in [Let's Encrypt integration](https://docs.gitlab.com/omnibus/settings/ssl.html#lets-encrypt-integration), and
|
||||
- You're using the built-in [Let's Encrypt integration](https://docs.gitlab.com/omnibus/settings/ssl.html#enable-the-lets-encrypt-integration), and
|
||||
- You're using GitLab 12.5 or later.
|
||||
|
||||
Otherwise, the Container Registry is not enabled. To enable it:
|
||||
|
@ -199,7 +199,7 @@ a wildcard certificate if hosted under a subdomain of your existing GitLab
|
|||
domain, for example, `registry.gitlab.example.com`.
|
||||
|
||||
As well as manually generated SSL certificates (explained here), certificates automatically
|
||||
generated by Let's Encrypt are also [supported in Omnibus installs](https://docs.gitlab.com/omnibus/settings/ssl.html#host-services).
|
||||
generated by Let's Encrypt are also [supported in Omnibus installs](https://docs.gitlab.com/omnibus/settings/ssl.html).
|
||||
|
||||
Let's assume that you want the container Registry to be accessible at
|
||||
`https://registry.gitlab.example.com`.
|
||||
|
|
|
@ -523,7 +523,7 @@ For Omnibus, this is fixed by [installing a custom CA in Omnibus GitLab](https:/
|
|||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/548) in GitLab 14.8.
|
||||
|
||||
If GitLab has been [configured to require mutual TLS](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-2-way-ssl-client-authentication), you need to add the client certificates to Pages:
|
||||
If GitLab has been [configured to require mutual TLS](https://docs.gitlab.com/omnibus/settings/ssl.html#enable-2-way-ssl-client-authentication), you need to add the client certificates to Pages:
|
||||
|
||||
1. Configure in `/etc/gitlab/gitlab.rb`:
|
||||
|
||||
|
|
|
@ -261,7 +261,7 @@ Configure your load balancer to pass connections on port 443 as `TCP` rather
|
|||
than `HTTP(S)` protocol. This will pass the connection to the application node's
|
||||
NGINX service untouched. NGINX will have the SSL certificate and listen on port 443.
|
||||
|
||||
See the [NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
|
||||
See the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
|
||||
for details on managing SSL certificates and configuring NGINX.
|
||||
|
||||
### Load balancer terminates SSL without backend SSL
|
||||
|
@ -272,7 +272,7 @@ terminating SSL.
|
|||
|
||||
Since communication between the load balancer and GitLab will not be secure,
|
||||
there is some additional configuration needed. See the
|
||||
[NGINX proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#supporting-proxied-ssl)
|
||||
[proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-a-reverse-proxy-or-load-balancer-ssl-termination)
|
||||
for details.
|
||||
|
||||
### Load balancer terminates SSL with backend SSL
|
||||
|
@ -285,7 +285,7 @@ Traffic will also be secure between the load balancers and NGINX in this
|
|||
scenario. There is no need to add configuration for proxied SSL since the
|
||||
connection will be secure all the way. However, configuration will need to be
|
||||
added to GitLab to configure SSL certificates. See
|
||||
[NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
|
||||
the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
|
||||
for details on managing SSL certificates and configuring NGINX.
|
||||
|
||||
### Readiness checks
|
||||
|
@ -2071,7 +2071,7 @@ On each node perform the following:
|
|||
When you specify `https` in the `external_url`, as in the previous example,
|
||||
GitLab expects that the SSL certificates are in `/etc/gitlab/ssl/`. If the
|
||||
certificates aren't present, NGINX will fail to start. For more information, see
|
||||
the [NGINX documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
|
||||
the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
|
||||
|
||||
### GitLab Rails post-configuration
|
||||
|
||||
|
|
|
@ -264,7 +264,7 @@ Configure your load balancer to pass connections on port 443 as `TCP` rather
|
|||
than `HTTP(S)` protocol. This will pass the connection to the application node's
|
||||
NGINX service untouched. NGINX will have the SSL certificate and listen on port 443.
|
||||
|
||||
See the [NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
|
||||
See the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
|
||||
for details on managing SSL certificates and configuring NGINX.
|
||||
|
||||
### Load balancer terminates SSL without backend SSL
|
||||
|
@ -275,7 +275,7 @@ terminating SSL.
|
|||
|
||||
Since communication between the load balancer and GitLab will not be secure,
|
||||
there is some additional configuration needed. See the
|
||||
[NGINX proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#supporting-proxied-ssl)
|
||||
[proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-a-reverse-proxy-or-load-balancer-ssl-termination)
|
||||
for details.
|
||||
|
||||
### Load balancer terminates SSL with backend SSL
|
||||
|
@ -287,8 +287,8 @@ end users will see.
|
|||
Traffic will also be secure between the load balancers and NGINX in this
|
||||
scenario. There is no need to add configuration for proxied SSL since the
|
||||
connection will be secure all the way. However, configuration will need to be
|
||||
added to GitLab to configure SSL certificates. See
|
||||
[NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
|
||||
added to GitLab to configure SSL certificates. See the
|
||||
[HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
|
||||
for details on managing SSL certificates and configuring NGINX.
|
||||
|
||||
### Readiness checks
|
||||
|
@ -2076,7 +2076,7 @@ On each node perform the following:
|
|||
When you specify `https` in the `external_url`, as in the previous example,
|
||||
GitLab expects that the SSL certificates are in `/etc/gitlab/ssl/`. If the
|
||||
certificates aren't present, NGINX will fail to start. For more information, see
|
||||
the [NGINX documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
|
||||
the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
|
||||
|
||||
### GitLab Rails post-configuration
|
||||
|
||||
|
|
|
@ -149,7 +149,7 @@ of `HTTP(S)`. This will pass the connection unaltered to the application node's
|
|||
NGINX service, which has the SSL certificate and listens to port 443.
|
||||
|
||||
For details about managing SSL certificates and configuring NGINX, see the
|
||||
[NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
|
||||
[HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
|
||||
|
||||
### Load balancer terminates SSL without backend SSL
|
||||
|
||||
|
@ -159,7 +159,7 @@ terminating SSL.
|
|||
|
||||
Due to communication between the load balancer and GitLab not being secure,
|
||||
you'll need to complete some additional configuration. For details, see the
|
||||
[NGINX proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#supporting-proxied-ssl).
|
||||
[proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-a-reverse-proxy-or-load-balancer-ssl-termination).
|
||||
|
||||
### Load balancer terminates SSL with backend SSL
|
||||
|
||||
|
@ -171,7 +171,7 @@ Traffic will be secure between the load balancers and NGINX in this scenario,
|
|||
and there's no need to add a configuration for proxied SSL. However, you'll
|
||||
need to add a configuration to GitLab to configure SSL certificates. For
|
||||
details about managing SSL certificates and configuring NGINX, see the
|
||||
[NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
|
||||
[HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
|
||||
|
||||
### Readiness checks
|
||||
|
||||
|
@ -752,7 +752,7 @@ On each node perform the following:
|
|||
When you specify `https` in the `external_url`, as in the previous example,
|
||||
GitLab expects that the SSL certificates are in `/etc/gitlab/ssl/`. If the
|
||||
certificates aren't present, NGINX will fail to start. For more information, see
|
||||
the [NGINX documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
|
||||
the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
|
||||
|
||||
### GitLab Rails post-configuration
|
||||
|
||||
|
|
|
@ -265,7 +265,7 @@ Configure your load balancer to pass connections on port 443 as `TCP` rather
|
|||
than `HTTP(S)` protocol. This will pass the connection to the application node's
|
||||
NGINX service untouched. NGINX will have the SSL certificate and listen on port 443.
|
||||
|
||||
See the [NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
|
||||
See the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
|
||||
for details on managing SSL certificates and configuring NGINX.
|
||||
|
||||
### Load balancer terminates SSL without backend SSL
|
||||
|
@ -276,7 +276,7 @@ terminating SSL.
|
|||
|
||||
Since communication between the load balancer and GitLab will not be secure,
|
||||
there is some additional configuration needed. See the
|
||||
[NGINX proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#supporting-proxied-ssl)
|
||||
[proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-a-reverse-proxy-or-load-balancer-ssl-termination)
|
||||
for details.
|
||||
|
||||
### Load balancer terminates SSL with backend SSL
|
||||
|
@ -288,8 +288,8 @@ end users will see.
|
|||
Traffic will also be secure between the load balancers and NGINX in this
|
||||
scenario. There is no need to add configuration for proxied SSL since the
|
||||
connection will be secure all the way. However, configuration will need to be
|
||||
added to GitLab to configure SSL certificates. See
|
||||
[NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
|
||||
added to GitLab to configure SSL certificates. See the
|
||||
[HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
|
||||
for details on managing SSL certificates and configuring NGINX.
|
||||
|
||||
### Readiness checks
|
||||
|
@ -2007,7 +2007,7 @@ On each node perform the following:
|
|||
When you specify `https` in the `external_url`, as in the previous example,
|
||||
GitLab expects that the SSL certificates are in `/etc/gitlab/ssl/`. If the
|
||||
certificates aren't present, NGINX will fail to start. For more information, see
|
||||
the [NGINX documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
|
||||
the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
|
||||
|
||||
### GitLab Rails post-configuration
|
||||
|
||||
|
|
|
@ -270,7 +270,7 @@ Configure your load balancer to pass connections on port 443 as `TCP` rather
|
|||
than `HTTP(S)` protocol. This will pass the connection to the application node's
|
||||
NGINX service untouched. NGINX will have the SSL certificate and listen on port 443.
|
||||
|
||||
See the [NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
|
||||
See the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
|
||||
for details on managing SSL certificates and configuring NGINX.
|
||||
|
||||
### Load balancer terminates SSL without backend SSL
|
||||
|
@ -281,7 +281,7 @@ terminating SSL.
|
|||
|
||||
Since communication between the load balancer and GitLab will not be secure,
|
||||
there is some additional configuration needed. See the
|
||||
[NGINX proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#supporting-proxied-ssl)
|
||||
[proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-a-reverse-proxy-or-load-balancer-ssl-termination)
|
||||
for details.
|
||||
|
||||
### Load balancer terminates SSL with backend SSL
|
||||
|
@ -293,8 +293,8 @@ end users will see.
|
|||
Traffic will also be secure between the load balancers and NGINX in this
|
||||
scenario. There is no need to add configuration for proxied SSL since the
|
||||
connection will be secure all the way. However, configuration will need to be
|
||||
added to GitLab to configure SSL certificates. See
|
||||
[NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
|
||||
added to GitLab to configure SSL certificates. See the
|
||||
[HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
|
||||
for details on managing SSL certificates and configuring NGINX.
|
||||
|
||||
### Readiness checks
|
||||
|
@ -2092,7 +2092,7 @@ On each node perform the following:
|
|||
When you specify `https` in the `external_url`, as in the previous example,
|
||||
GitLab expects that the SSL certificates are in `/etc/gitlab/ssl/`. If the
|
||||
certificates aren't present, NGINX will fail to start. For more information, see
|
||||
the [NGINX documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
|
||||
the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
|
||||
|
||||
### GitLab Rails post-configuration
|
||||
|
||||
|
|
|
@ -263,7 +263,7 @@ Configure your load balancer to pass connections on port 443 as `TCP` rather
|
|||
than `HTTP(S)` protocol. This passes the connection to the application node's
|
||||
NGINX service untouched. NGINX has the SSL certificate and listen on port 443.
|
||||
|
||||
See the [NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
|
||||
See the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
|
||||
for details on managing SSL certificates and configuring NGINX.
|
||||
|
||||
### Load balancer terminates SSL without backend SSL
|
||||
|
@ -274,7 +274,7 @@ terminating SSL.
|
|||
|
||||
Since communication between the load balancer and GitLab is not secure,
|
||||
there is some additional configuration needed. See the
|
||||
[NGINX proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#supporting-proxied-ssl)
|
||||
[proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-a-reverse-proxy-or-load-balancer-ssl-termination)
|
||||
for details.
|
||||
|
||||
### Load balancer terminates SSL with backend SSL
|
||||
|
@ -286,8 +286,8 @@ end users see.
|
|||
Traffic is also secure between the load balancers and NGINX in this
|
||||
scenario. There is no need to add configuration for proxied SSL since the
|
||||
connection is secure all the way. However, configuration needs to be
|
||||
added to GitLab to configure SSL certificates. See
|
||||
[NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
|
||||
added to GitLab to configure SSL certificates. See the
|
||||
[HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
|
||||
for details on managing SSL certificates and configuring NGINX.
|
||||
|
||||
### Readiness checks
|
||||
|
@ -2007,7 +2007,7 @@ On each node perform the following:
|
|||
When you specify `https` in the `external_url`, as in the previous example,
|
||||
GitLab expects that the SSL certificates are in `/etc/gitlab/ssl/`. If the
|
||||
certificates aren't present, NGINX fails to start. For more information, see
|
||||
the [NGINX documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
|
||||
the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
|
||||
|
||||
### GitLab Rails post-configuration
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ main SSL documentation:
|
|||
|
||||
- [Omnibus SSL Configuration](https://docs.gitlab.com/omnibus/settings/ssl.html).
|
||||
- [Self-signed certificates or custom Certification Authorities for GitLab Runner](https://docs.gitlab.com/runner/configuration/tls-self-signed.html).
|
||||
- [Manually configuring HTTPS](https://docs.gitlab.com/omnibus/settings/nginx.html#manually-configuring-https).
|
||||
- [Configure HTTPS manually](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-https-manually).
|
||||
|
||||
## Using an internal CA certificate with GitLab
|
||||
|
||||
|
|
|
@ -80,6 +80,11 @@ Example response:
|
|||
}
|
||||
```
|
||||
|
||||
## Container Registry pagination
|
||||
|
||||
By default, `GET` requests return 20 results at a time because the API results
|
||||
are [paginated](index.md#pagination).
|
||||
|
||||
## List registry repositories
|
||||
|
||||
### Within a project
|
||||
|
|
|
@ -289,6 +289,34 @@ preserving deployment keys and other credentials from being unintentionally
|
|||
accessed. To ensure that jobs intended to be executed on protected
|
||||
runners do not use regular runners, they must be tagged accordingly.
|
||||
|
||||
## Trigger a pipeline when an upstream project is rebuilt **(PREMIUM)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9045) in GitLab 12.8.
|
||||
|
||||
You can trigger a pipeline in your project whenever a pipeline finishes for a new
|
||||
tag in a different project.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- The upstream project must be [public](../../user/public_access.md).
|
||||
- The user must have the Developer role
|
||||
in the upstream project.
|
||||
|
||||
To trigger the pipeline when the upstream project is rebuilt:
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Settings > CI/CD**.
|
||||
1. Expand **Pipeline subscriptions**.
|
||||
1. Enter the project you want to subscribe to, in the format `<namespace>/<project>`.
|
||||
For example, if the project is `https://gitlab.com/gitlab-org/gitlab`, use `gitlab-org/gitlab`.
|
||||
1. Select **Subscribe**.
|
||||
|
||||
Any pipelines that complete successfully for new tags in the subscribed project
|
||||
now trigger a pipeline on the current project's default branch. The maximum
|
||||
number of upstream pipeline subscriptions is 2 by default, for both the upstream and
|
||||
downstream projects. On self-managed instances, an administrator can change this
|
||||
[limit](../../administration/instance_limits.md#number-of-cicd-subscriptions-to-a-project).
|
||||
|
||||
### How pipeline duration is calculated
|
||||
|
||||
Total running time for a given pipeline excludes retries and pending
|
||||
|
|
|
@ -244,34 +244,6 @@ When using:
|
|||
- [`only/except`](../yaml/index.md#only--except) to control job behavior, use the
|
||||
`pipelines` keyword.
|
||||
|
||||
### Trigger a pipeline when an upstream project is rebuilt **(PREMIUM)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9045) in GitLab 12.8.
|
||||
|
||||
You can trigger a pipeline in your project whenever a pipeline finishes for a new
|
||||
tag in a different project.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- The upstream project must be [public](../../user/public_access.md).
|
||||
- The user must have the Developer role
|
||||
in the upstream project.
|
||||
|
||||
To trigger the pipeline when the upstream project is rebuilt:
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Settings > CI/CD**.
|
||||
1. Expand **Pipeline subscriptions**.
|
||||
1. Enter the project you want to subscribe to, in the format `<namespace>/<project>`.
|
||||
For example, if the project is `https://gitlab.com/gitlab-org/gitlab`, use `gitlab-org/gitlab`.
|
||||
1. Select **Subscribe**.
|
||||
|
||||
Any pipelines that complete successfully for new tags in the subscribed project
|
||||
now trigger a pipeline on the current project's default branch. The maximum
|
||||
number of upstream pipeline subscriptions is 2 by default, for both the upstream and
|
||||
downstream projects. On self-managed instances, an administrator can change this
|
||||
[limit](../../administration/instance_limits.md#number-of-cicd-subscriptions-to-a-project).
|
||||
|
||||
## Multi-project pipeline visualization **(PREMIUM)**
|
||||
|
||||
When your pipeline triggers a downstream pipeline, the downstream pipeline displays
|
||||
|
|
|
@ -37,7 +37,7 @@ On the left side we have the events that can trigger a pipeline based on various
|
|||
- When a [merge request is created or updated](../../ci/pipelines/merge_request_pipelines.md).
|
||||
- When an MR is added to a [Merge Train](../../ci/pipelines/merge_trains.md#merge-trains).
|
||||
- A [scheduled pipeline](../../ci/pipelines/schedules.md).
|
||||
- When project is [subscribed to an upstream project](../../ci/pipelines/multi_project_pipelines.md#trigger-a-pipeline-when-an-upstream-project-is-rebuilt).
|
||||
- When project is [subscribed to an upstream project](../../ci/pipelines/index.md#trigger-a-pipeline-when-an-upstream-project-is-rebuilt).
|
||||
- When [Auto DevOps](../../topics/autodevops/index.md) is enabled.
|
||||
- When GitHub integration is used with [external pull requests](../../ci/ci_cd_for_external_repos/index.md#pipelines-for-external-pull-requests).
|
||||
- When an upstream pipeline contains a [bridge job](../../ci/yaml/index.md#trigger) which triggers a downstream pipeline.
|
||||
|
|
|
@ -355,18 +355,6 @@ as a reviewer, it is recommended that they are not also picked as the maintainer
|
|||
Maintainers should check before merging if the merge request is approved by the
|
||||
required approvers. If still awaiting further approvals from others, remove yourself as a reviewer then `@` mention the author and explain why in a comment. Stay as reviewer if you're merging the code.
|
||||
|
||||
Maintainers must check before merging if the merge request is introducing new
|
||||
vulnerabilities, by inspecting the list in the merge request
|
||||
[Security Widget](../user/application_security/index.md).
|
||||
When in doubt, a [Security Engineer](https://about.gitlab.com/company/team/) can be involved. The list of detected
|
||||
vulnerabilities must be either empty or containing:
|
||||
|
||||
- dismissed vulnerabilities in case of false positives
|
||||
- vulnerabilities converted to issues
|
||||
|
||||
Maintainers should **never** dismiss vulnerabilities to "empty" the list,
|
||||
without duly verifying them.
|
||||
|
||||
Note that certain merge requests may target a stable branch. These are rare
|
||||
events. These types of merge requests cannot be merged by the Maintainer.
|
||||
Instead, these should be sent to the [Release Manager](https://about.gitlab.com/community/release-managers/).
|
||||
|
|
|
@ -10,6 +10,8 @@ The DeclarativePolicy framework is designed to assist in performance of policy c
|
|||
|
||||
The policy used is based on the subject's class name - so `Ability.allowed?(user, :some_ability, project)` creates a `ProjectPolicy` and check permissions on that.
|
||||
|
||||
The Ruby gem source is available in the [declarative-policy](https://gitlab.com/gitlab-org/ruby/gems/declarative-policy) GitLab project.
|
||||
|
||||
## Managing Permission Rules
|
||||
|
||||
Permissions are broken into two parts: `conditions` and `rules`. Conditions are boolean expressions that can access the database and the environment, while rules are statically configured combinations of expressions and other rules that enable or prevent certain abilities. For an ability to be allowed, it must be enabled by at least one rule, and not prevented by any.
|
||||
|
|
|
@ -204,3 +204,33 @@ provide a smooth migration path of epics to WIT with minimal disruption to user
|
|||
|
||||
We will move towards work items, work item types, and custom widgets (CW) in an iterative process.
|
||||
For a rough outline of the work ahead of us, see [epic 6033](https://gitlab.com/groups/gitlab-org/-/epics/6033).
|
||||
|
||||
## Redis HLL Counter Schema
|
||||
|
||||
We need a more scalable Redis counter schema for work items that is inclusive of Plan xMAU, Project Management xMAU, Certify xMAU, and
|
||||
Product Planning xMAU. We cannot aggregate and dedupe events across features within a group or at the stage level with
|
||||
our current Redis slot schema.
|
||||
|
||||
All three Plan product groups will be using the same base object (`work item`). Each product group still needs to
|
||||
track MAU.
|
||||
|
||||
### Proposed aggregate counter schema
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
Event[Specific Interaction Counter] --> AC[Aggregate Counters]
|
||||
AC --> Plan[Plan xMAU]
|
||||
AC --> PM[Project Management xMAU]
|
||||
AC --> PP[Product Planning xMAU]
|
||||
AC --> Cer[Certify xMAU]
|
||||
AC --> WI[Work Items Users]
|
||||
```
|
||||
|
||||
### Implementation
|
||||
|
||||
The new aggregate schema is already implemented and we are already tracking work item unique actions
|
||||
in [GitLab.com](https://gitlab.com).
|
||||
|
||||
For implementation details, this [MR](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93231) can be used
|
||||
as a reference. The MR covers the definition of new unique actions, event tracking in the code and also
|
||||
adding the new unique actions to the required aggregate counters.
|
||||
|
|
|
@ -483,7 +483,7 @@ Connect to your GitLab instance via **Bastion Host A** using [SSH Agent Forwardi
|
|||
|
||||
#### Disable Let's Encrypt
|
||||
|
||||
Because we're adding our SSL certificate at the load balancer, we do not need the GitLab built-in support for Let's Encrypt. Let's Encrypt [is enabled by default](https://docs.gitlab.com/omnibus/settings/ssl.html#lets-encrypt-integration) when using an `https` domain in GitLab 10.7 and later, so we must explicitly disable it:
|
||||
Because we're adding our SSL certificate at the load balancer, we do not need the GitLab built-in support for Let's Encrypt. Let's Encrypt [is enabled by default](https://docs.gitlab.com/omnibus/settings/ssl.html#enable-the-lets-encrypt-integration) when using an `https` domain in GitLab 10.7 and later, so we must explicitly disable it:
|
||||
|
||||
1. Open `/etc/gitlab/gitlab.rb` and disable it:
|
||||
|
||||
|
@ -605,7 +605,7 @@ Now that we have our EC2 instance ready, follow the [documentation to install Gi
|
|||
|
||||
#### Add Support for Proxied SSL
|
||||
|
||||
As we are terminating SSL at our [load balancer](#load-balancer), follow the steps at [Supporting proxied SSL](https://docs.gitlab.com/omnibus/settings/nginx.html#supporting-proxied-ssl) to configure this in `/etc/gitlab/gitlab.rb`.
|
||||
As we are terminating SSL at our [load balancer](#load-balancer), follow the steps at [Supporting proxied SSL](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-a-reverse-proxy-or-load-balancer-ssl-termination) to configure this in `/etc/gitlab/gitlab.rb`.
|
||||
|
||||
Remember to run `sudo gitlab-ctl reconfigure` after saving the changes to the `gitlab.rb` file.
|
||||
|
||||
|
|
|
@ -301,7 +301,7 @@ point to a valid URL.
|
|||
To receive e-mails from GitLab you have to configure the
|
||||
[SMTP settings](https://docs.gitlab.com/omnibus/settings/smtp.html) because the GitLab Docker image doesn't
|
||||
have an SMTP server installed. You may also be interested in
|
||||
[enabling HTTPS](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
|
||||
[enabling HTTPS](https://docs.gitlab.com/omnibus/settings/ssl.html).
|
||||
|
||||
After you make all the changes you want, you will need to restart the container
|
||||
in order to reconfigure GitLab:
|
||||
|
|
|
@ -117,8 +117,8 @@ here's how you configure GitLab to be aware of the change:
|
|||
|
||||
### Configuring HTTPS with the domain name
|
||||
|
||||
Although not needed, it's strongly recommended to secure GitLab with a TLS
|
||||
certificate. Follow the steps in the [Omnibus documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
|
||||
Although not needed, it's strongly recommended to secure GitLab with a
|
||||
[TLS certificate](https://docs.gitlab.com/omnibus/settings/ssl.html).
|
||||
|
||||
### Configuring the email SMTP settings
|
||||
|
||||
|
|
|
@ -58,8 +58,9 @@ There are a couple of ways to achieve that:
|
|||
::Gitlab::CurrentSettings.elasticsearch_limit_indexing? # Whether or not Elasticsearch is limited only to certain projects/namespaces
|
||||
```
|
||||
|
||||
- Confirm searches use Elasticsearch by accessing the [rails console]
|
||||
(../../administration/operations/rails_console.md) and running the following commands:
|
||||
- Confirm searches use Elasticsearch by accessing the
|
||||
[rails console](../../administration/operations/rails_console.md) and running the following
|
||||
commands:
|
||||
|
||||
```rails
|
||||
u = User.find_by_email('email_of_user_doing_search')
|
||||
|
|
|
@ -174,7 +174,7 @@ Error obtaining access token. Cannot access https://gitlab.example.com from Jira
|
|||
- The [GitLab Jira integration](index.md) requires
|
||||
GitLab to connect to Jira. Any TLS issues that arise from a private certificate
|
||||
authority or self-signed certificate are resolved
|
||||
[on the GitLab server](https://docs.gitlab.com/omnibus/settings/ssl.html#other-certificate-authorities),
|
||||
[on the GitLab server](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates),
|
||||
as GitLab is the TLS client.
|
||||
- The Jira Development panel integration requires Jira to connect to GitLab, which
|
||||
causes Jira to be the TLS client. If your GitLab server's certificate is not
|
||||
|
|
|
@ -71,7 +71,7 @@ sudo EXTERNAL_URL="http://my-host.internal" dpkg -i <gitlab_package_name>.deb
|
|||
## Enabling SSL
|
||||
|
||||
Follow these steps to enable SSL for your fresh instance. These steps reflect those for
|
||||
[manually configuring SSL in Omnibus's NGINX configuration](https://docs.gitlab.com/omnibus/settings/nginx.html#manually-configuring-https):
|
||||
[manually configuring SSL in Omnibus's NGINX configuration](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-https-manually):
|
||||
|
||||
1. Make the following changes to `/etc/gitlab/gitlab.rb`:
|
||||
|
||||
|
|
|
@ -1749,11 +1749,11 @@ When checking if a runner is `paused`, API users are advised to check the boolea
|
|||
|
||||
</div>
|
||||
|
||||
<div class="deprecation removal-156 breaking-change">
|
||||
<div class="deprecation removal-159 breaking-change">
|
||||
|
||||
### SaaS certificate-based integration with Kubernetes
|
||||
|
||||
Planned removal: GitLab <span class="removal-milestone">15.6</span> (2022-11-22)
|
||||
Planned removal: GitLab <span class="removal-milestone">15.9</span> (2023-02-22)
|
||||
|
||||
WARNING:
|
||||
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
|
||||
|
|
|
@ -30,7 +30,7 @@ Prerequisites:
|
|||
|
||||
You can set the issue weight when you create or edit an issue.
|
||||
|
||||
You must use whole numbers (like 0, 1, 2). Negative numbers or fractions are not accepted.
|
||||
You must enter whole, positive numbers.
|
||||
|
||||
When you change the weight of an issue, the new value overwrites the previous value.
|
||||
|
||||
|
|
|
@ -57,8 +57,6 @@ switch between ascending or descending order, select **Sort order**.
|
|||
|
||||
## Create a release
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32812) in GitLab 12.9. Releases can be created directly in the GitLab UI.
|
||||
|
||||
You can create a release:
|
||||
|
||||
- [Using a job in your CI/CD pipeline](#creating-a-release-by-using-a-cicd-job).
|
||||
|
@ -68,13 +66,13 @@ You can create a release:
|
|||
|
||||
We recommend creating a release as one of the last steps in your CI/CD pipeline.
|
||||
|
||||
### Create a release in the Releases page
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Developer role for a project. For more information, read
|
||||
[Release permissions](#release-permissions).
|
||||
|
||||
### Create a release in the Releases page
|
||||
|
||||
To create a release in the Releases page:
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
|
@ -124,8 +122,11 @@ You can create a release directly as part of the GitLab CI/CD pipeline by using
|
|||
The release is created only if the job processes without error. If the API returns an error during
|
||||
release creation, the release job fails.
|
||||
|
||||
For examples of how you can create a release of your application in the CI/CD pipeline,
|
||||
see [Release CI/CD examples](release_cicd_examples.md).
|
||||
Methods for creating a release using a CI/CD job include:
|
||||
|
||||
- [Create a release when a Git tag is created](release_cicd_examples.md#create-a-release-when-a-git-tag-is-created).
|
||||
- [Create a release when a commit is merged to the default branch](release_cicd_examples.md#create-a-release-when-a-commit-is-merged-to-the-default-branch).
|
||||
- [Create release metadata in a custom script](release_cicd_examples.md#create-release-metadata-in-a-custom-script).
|
||||
|
||||
### Use a custom SSL CA certificate authority
|
||||
|
||||
|
|
|
@ -12,9 +12,13 @@ CI/CD pipeline.
|
|||
|
||||
## Create a release when a Git tag is created
|
||||
|
||||
In this CI/CD example, pushing a Git tag to the repository, or creating a Git tag in the UI triggers
|
||||
the release. You can use this method if you prefer to create the Git tag manually, and create a
|
||||
release as a result.
|
||||
In this CI/CD example, the release is triggered by one of the following events:
|
||||
|
||||
- Pushing a Git tag to the repository.
|
||||
- Creating a Git tag in the UI.
|
||||
|
||||
You can use this method if you prefer to create the Git tag manually, and create a release as a
|
||||
result.
|
||||
|
||||
NOTE:
|
||||
Do not provide Release notes when you create the Git tag in the UI. Providing release notes
|
||||
|
@ -40,8 +44,8 @@ release_job:
|
|||
|
||||
## Create a release when a commit is merged to the default branch
|
||||
|
||||
In this CI/CD example, merging a commit to the default branch triggers the pipeline. You can use
|
||||
this method if your release workflow does not create a tag manually.
|
||||
In this CI/CD example, the release is triggered when you merge a commit to the default branch. You
|
||||
can use this method if your release workflow does not create a tag manually.
|
||||
|
||||
Key points in the following _extract_ of an example `.gitlab-ci.yml` file:
|
||||
|
||||
|
@ -69,16 +73,75 @@ Environment variables set in `before_script` or `script` are not available for e
|
|||
in the same job. Read more about
|
||||
[potentially making variables available for expanding](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/6400).
|
||||
|
||||
## Create release metadata in a custom script
|
||||
|
||||
In this CI/CD example the release preparation is split into separate jobs for greater flexibility:
|
||||
|
||||
- The `prepare_job` job generates the release metadata. Any image can be used to run the job,
|
||||
including a custom image. The generated metadata is stored in the variable file `variables.env`.
|
||||
This metadata is [passed to the downstream job](../../../ci/variables/index.md#pass-an-environment-variable-to-another-job).
|
||||
- The `release_job` uses the content from the variables file to create a release, using the
|
||||
metadata passed to it in the variables file. This job must use the
|
||||
`registry.gitlab.com/gitlab-org/release-cli:latest` image because it contains the release CLI.
|
||||
|
||||
```yaml
|
||||
prepare_job:
|
||||
stage: prepare # This stage must run before the release stage
|
||||
rules:
|
||||
- if: $CI_COMMIT_TAG
|
||||
when: never # Do not run this job when a tag is created manually
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Run this job when commits are pushed or merged to the default branch
|
||||
script:
|
||||
- echo "EXTRA_DESCRIPTION=some message" >> variables.env # Generate the EXTRA_DESCRIPTION and TAG environment variables
|
||||
- echo "TAG=v$(cat VERSION)" >> variables.env # and append to the variables.env file
|
||||
artifacts:
|
||||
reports:
|
||||
dotenv: variables.env # Use artifacts:reports:dotenv to expose the variables to other jobs
|
||||
|
||||
release_job:
|
||||
stage: release
|
||||
image: registry.gitlab.com/gitlab-org/release-cli:latest
|
||||
needs:
|
||||
- job: prepare_job
|
||||
artifacts: true
|
||||
rules:
|
||||
- if: $CI_COMMIT_TAG
|
||||
when: never # Do not run this job when a tag is created manually
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Run this job when commits are pushed or merged to the default branch
|
||||
script:
|
||||
- echo "running release_job for $TAG"
|
||||
release:
|
||||
name: 'Release $TAG'
|
||||
description: 'Created using the release-cli $EXTRA_DESCRIPTION' # $EXTRA_DESCRIPTION and the $TAG
|
||||
tag_name: '$TAG' # variables must be defined elsewhere
|
||||
ref: '$CI_COMMIT_SHA' # in the pipeline. For example, in the
|
||||
milestones: # prepare_job
|
||||
- 'm1'
|
||||
- 'm2'
|
||||
- 'm3'
|
||||
released_at: '2020-07-15T08:00:00Z' # Optional, is auto generated if not defined, or can use a variable.
|
||||
assets:
|
||||
links:
|
||||
- name: 'asset1'
|
||||
url: 'https://example.com/assets/1'
|
||||
- name: 'asset2'
|
||||
url: 'https://example.com/assets/2'
|
||||
filepath: '/pretty/url/1' # optional
|
||||
link_type: 'other' # optional
|
||||
```
|
||||
|
||||
## Skip multiple pipelines when creating a release
|
||||
|
||||
Creating a release using a CI/CD job could potentially trigger multiple pipelines if the associated tag does not exist already. To understand how this might happen, consider the following workflows:
|
||||
|
||||
- Tag first, release second:
|
||||
|
||||
1. A tag is created via UI or pushed.
|
||||
1. A tag pipeline is triggered, and runs `release` job.
|
||||
1. A release is created.
|
||||
|
||||
- Release first, tag second:
|
||||
|
||||
1. A pipeline is triggered when commits are pushed or merged to default branch. The pipeline runs `release` job.
|
||||
1. A release is created.
|
||||
1. A tag is created.
|
||||
|
|
|
@ -420,7 +420,7 @@ to move any project to any namespace.
|
|||
When you transfer a project from a namespace licensed for GitLab SaaS Premium or Ultimate to GitLab Free, the following paid feature data is deleted:
|
||||
|
||||
- [Project access tokens](../../../user/project/settings/project_access_tokens.md) are revoked
|
||||
- [Pipeline subscriptions](../../../ci/pipelines/multi_project_pipelines.md#trigger-a-pipeline-when-an-upstream-project-is-rebuilt)
|
||||
- [Pipeline subscriptions](../../../ci/pipelines/index.md#trigger-a-pipeline-when-an-upstream-project-is-rebuilt)
|
||||
and [test cases](../../../ci/test_cases/index.md) are deleted.
|
||||
|
||||
## Delete a project
|
||||
|
|
|
@ -99,3 +99,21 @@ To delete a task:
|
|||
1. In the issue description, in the **Tasks** section, select the task you want to edit.
|
||||
1. In the task window, in the options menu (**{ellipsis_v}**), select **Delete task**.
|
||||
1. Select **OK**.
|
||||
|
||||
## Set task weight **PREMIUM**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/362550) in GitLab 15.3.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Reporter role for the project.
|
||||
|
||||
You can set weight on each task to show how much work it needs.
|
||||
This value is visible only when you view a task.
|
||||
|
||||
To set issue weight of a task:
|
||||
|
||||
1. In the issue description, in the **Tasks** section, select the title of the task you want to edit.
|
||||
The task window opens.
|
||||
1. Next to **Weight**, enter a whole, positive number.
|
||||
1. Select the close icon (**{close}**).
|
||||
|
|
|
@ -146,7 +146,8 @@ module API
|
|||
|
||||
branch = find_branch!(params[:branch])
|
||||
protected_branch = user_project.protected_branches.find_by(name: branch.name)
|
||||
protected_branch&.destroy
|
||||
|
||||
::ProtectedBranches::DestroyService.new(user_project, current_user).execute(protected_branch) if protected_branch
|
||||
|
||||
present branch, with: Entities::Branch, current_user: current_user, project: user_project
|
||||
end
|
||||
|
|
|
@ -149,6 +149,10 @@ module Gitlab
|
|||
severity_mapping.fetch(severity_raw.to_s.downcase, UNMAPPED_SEVERITY)
|
||||
end
|
||||
|
||||
def source
|
||||
monitoring_tool || integration&.name
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def plain_gitlab_fingerprint
|
||||
|
|
|
@ -6,6 +6,7 @@ module Gitlab
|
|||
module Payload
|
||||
class Generic < Base
|
||||
DEFAULT_TITLE = 'New: Alert'
|
||||
DEFAULT_SOURCE = 'Generic Alert Endpoint'
|
||||
|
||||
attribute :description, paths: 'description'
|
||||
attribute :ends_at, paths: 'end_time', type: :time
|
||||
|
@ -22,6 +23,14 @@ module Gitlab
|
|||
|
||||
attribute :plain_gitlab_fingerprint, paths: 'fingerprint'
|
||||
private :plain_gitlab_fingerprint
|
||||
|
||||
def resolved?
|
||||
ends_at.present?
|
||||
end
|
||||
|
||||
def source
|
||||
super || DEFAULT_SOURCE
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -44414,9 +44414,6 @@ msgstr ""
|
|||
msgid "Work in progress Limit"
|
||||
msgstr ""
|
||||
|
||||
msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
|
||||
msgstr ""
|
||||
|
||||
msgid "WorkItem|Add"
|
||||
msgstr ""
|
||||
|
||||
|
@ -44476,7 +44473,7 @@ msgstr ""
|
|||
msgid "WorkItem|Issue"
|
||||
msgstr ""
|
||||
|
||||
msgid "WorkItem|Learn about tasks"
|
||||
msgid "WorkItem|Learn about tasks."
|
||||
msgstr ""
|
||||
|
||||
msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
|
||||
|
@ -44554,15 +44551,15 @@ msgstr ""
|
|||
msgid "WorkItem|Undo"
|
||||
msgstr ""
|
||||
|
||||
msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
|
||||
msgstr ""
|
||||
|
||||
msgid "WorkItem|Work Items"
|
||||
msgstr ""
|
||||
|
||||
msgid "WorkItem|Work item deleted"
|
||||
msgstr ""
|
||||
|
||||
msgid "WorkItem|work items"
|
||||
msgstr ""
|
||||
|
||||
msgid "Would you like to create a new branch?"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
"@tiptap/core": "^2.0.0-beta.182",
|
||||
"@tiptap/extension-blockquote": "^2.0.0-beta.29",
|
||||
"@tiptap/extension-bold": "^2.0.0-beta.28",
|
||||
"@tiptap/extension-bubble-menu": "^2.0.0-beta.61",
|
||||
"@tiptap/extension-bullet-list": "^2.0.0-beta.29",
|
||||
"@tiptap/extension-code": "^2.0.0-beta.28",
|
||||
"@tiptap/extension-code-block-lowlight": "2.0.0-beta.73",
|
||||
|
|
|
@ -51,7 +51,8 @@ RSpec.describe "Help Dropdown", :js do
|
|||
visit root_path
|
||||
end
|
||||
|
||||
it 'renders correct version badge variant' do
|
||||
it 'renders correct version badge variant',
|
||||
quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/369850' do
|
||||
page.within '.header-help' do
|
||||
find('.header-help-dropdown-toggle').click
|
||||
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
|
||||
import addBlameLink from '~/blob/blob_blame_link';
|
||||
|
||||
describe('Blob links', () => {
|
||||
const mouseoverEvent = new MouseEvent('mouseover', {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
setHTMLFixture(`
|
||||
<div id="blob-content-holder">
|
||||
<div class="line-numbers" data-blame-path="/blamePath">
|
||||
<a id="L5" href="#L5" data-line-number="5" class="file-line-num js-line-links">5</a>
|
||||
</div>
|
||||
<pre id="LC5">Line 5 content</pre>
|
||||
</div>
|
||||
`);
|
||||
|
||||
addBlameLink('#blob-content-holder', 'js-line-links');
|
||||
document.querySelector('.file-line-num').dispatchEvent(mouseoverEvent);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetHTMLFixture();
|
||||
});
|
||||
|
||||
it('adds wrapper elements with correct classes', () => {
|
||||
const wrapper = document.querySelector('.line-links');
|
||||
|
||||
expect(wrapper).toBeTruthy();
|
||||
expect(wrapper.classList).toContain('diff-line-num');
|
||||
});
|
||||
|
||||
it('adds blame link with correct classes and path', () => {
|
||||
const blameLink = document.querySelector('.file-line-blame');
|
||||
expect(blameLink).toBeTruthy();
|
||||
expect(blameLink.getAttribute('href')).toBe('/blamePath#L5');
|
||||
});
|
||||
|
||||
it('adds line link within wraper with correct classes and path', () => {
|
||||
const lineLink = document.querySelector('.file-line-num');
|
||||
expect(lineLink).toBeTruthy();
|
||||
expect(lineLink.getAttribute('href')).toBe('#L5');
|
||||
});
|
||||
});
|
|
@ -15,7 +15,7 @@ describe('Blob links Tracking', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
setHTMLFixture(`
|
||||
<div id="blob-content-holder">
|
||||
<div class="file-holder">
|
||||
<div class="line-links diff-line-num">
|
||||
<a href="#L5" class="file-line-blame"></a>
|
||||
<a id="L5" href="#L5" data-line-number="5" class="file-line-num">5</a>
|
||||
|
@ -23,7 +23,7 @@ describe('Blob links Tracking', () => {
|
|||
<pre id="LC5">Line 5 content</pre>
|
||||
</div>
|
||||
`);
|
||||
addBlobLinksTracking('#blob-content-holder', eventsToTrack);
|
||||
addBlobLinksTracking();
|
||||
jest.spyOn(Tracking, 'event');
|
||||
});
|
||||
|
||||
|
|
|
@ -4,8 +4,7 @@ import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
|
|||
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import BoardCardMoveToPosition from '~/boards/components/board_card_move_to_position.vue';
|
||||
import { createStore } from '~/boards/stores';
|
||||
import { mockList, mockIssue2 } from 'jest/boards/mock_data';
|
||||
import { mockList, mockIssue2, mockIssue, mockIssue3, mockIssue4 } from 'jest/boards/mock_data';
|
||||
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
@ -21,7 +20,26 @@ describe('Board Card Move to position', () => {
|
|||
let store;
|
||||
let dispatch;
|
||||
|
||||
store = new Vuex.Store();
|
||||
const createStoreOptions = () => {
|
||||
const state = {
|
||||
pageInfoByListId: {
|
||||
'gid://gitlab/List/1': {},
|
||||
'gid://gitlab/List/2': { hasNextPage: true },
|
||||
},
|
||||
};
|
||||
const getters = {
|
||||
getBoardItemsByList: () => () => [mockIssue, mockIssue2, mockIssue3, mockIssue4],
|
||||
};
|
||||
const actions = {
|
||||
moveItem: jest.fn(),
|
||||
};
|
||||
|
||||
return {
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
};
|
||||
};
|
||||
|
||||
const createComponent = (propsData) => {
|
||||
wrapper = shallowMountExtended(BoardCardMoveToPosition, {
|
||||
|
@ -40,7 +58,7 @@ describe('Board Card Move to position', () => {
|
|||
};
|
||||
|
||||
beforeEach(() => {
|
||||
store = createStore();
|
||||
store = new Vuex.Store(createStoreOptions());
|
||||
createComponent();
|
||||
});
|
||||
|
||||
|
@ -62,13 +80,26 @@ describe('Board Card Move to position', () => {
|
|||
|
||||
it('is opened on the click of vertical ellipsis and has 2 dropdown items when number of list items < 10', () => {
|
||||
findMoveToPositionDropdown().vm.$emit('click');
|
||||
|
||||
expect(findDropdownItems()).toHaveLength(dropdownOptions.length);
|
||||
});
|
||||
|
||||
it('is opened on the click of vertical ellipsis and has 1 dropdown items when number of list items > 10', () => {
|
||||
wrapper.destroy();
|
||||
|
||||
createComponent({
|
||||
list: {
|
||||
...mockList,
|
||||
id: 'gid://gitlab/List/2',
|
||||
},
|
||||
});
|
||||
findMoveToPositionDropdown().vm.$emit('click');
|
||||
expect(findDropdownItems()).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Dropdown options', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ index: 1 });
|
||||
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
|
||||
dispatch = jest.spyOn(store, 'dispatch').mockImplementation(() => {});
|
||||
});
|
||||
|
@ -78,12 +109,12 @@ describe('Board Card Move to position', () => {
|
|||
});
|
||||
|
||||
it.each`
|
||||
dropdownIndex | dropdownLabel | startActionCalledTimes | trackLabel
|
||||
${0} | ${BoardCardMoveToPosition.i18n.moveToStartText} | ${0} | ${'move_to_start'}
|
||||
${1} | ${BoardCardMoveToPosition.i18n.moveToEndText} | ${1} | ${'move_to_end'}
|
||||
dropdownIndex | dropdownLabel | trackLabel | moveAfterId | moveBeforeId
|
||||
${0} | ${BoardCardMoveToPosition.i18n.moveToStartText} | ${'move_to_start'} | ${mockIssue.id} | ${undefined}
|
||||
${1} | ${BoardCardMoveToPosition.i18n.moveToEndText} | ${'move_to_end'} | ${undefined} | ${mockIssue4.id}
|
||||
`(
|
||||
'on click of dropdown index $dropdownIndex with label $dropdownLabel should call moveItem action with tracking label $trackLabel',
|
||||
async ({ dropdownIndex, startActionCalledTimes, dropdownLabel, trackLabel }) => {
|
||||
async ({ dropdownIndex, dropdownLabel, trackLabel, moveAfterId, moveBeforeId }) => {
|
||||
await findEllipsesButton().vm.$emit('click');
|
||||
|
||||
expect(findDropdownItemAtIndex(dropdownIndex).text()).toBe(dropdownLabel);
|
||||
|
@ -98,18 +129,15 @@ describe('Board Card Move to position', () => {
|
|||
label: trackLabel,
|
||||
property: 'type_card',
|
||||
});
|
||||
expect(dispatch).toHaveBeenCalledTimes(startActionCalledTimes);
|
||||
if (startActionCalledTimes) {
|
||||
expect(dispatch).toHaveBeenCalledWith('moveItem', {
|
||||
fromListId: mockList.id,
|
||||
itemId: mockIssue2.id,
|
||||
itemIid: mockIssue2.iid,
|
||||
itemPath: mockIssue2.referencePath,
|
||||
moveBeforeId: undefined,
|
||||
moveAfterId: undefined,
|
||||
toListId: mockList.id,
|
||||
});
|
||||
}
|
||||
expect(dispatch).toHaveBeenCalledWith('moveItem', {
|
||||
fromListId: mockList.id,
|
||||
itemId: mockIssue2.id,
|
||||
itemIid: mockIssue2.iid,
|
||||
itemPath: mockIssue2.referencePath,
|
||||
moveBeforeId,
|
||||
moveAfterId,
|
||||
toListId: mockList.id,
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
import { BubbleMenuPlugin } from '@tiptap/extension-bubble-menu';
|
||||
import { nextTick } from 'vue';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import BubbleMenu from '~/content_editor/components/bubble_menus/bubble_menu.vue';
|
||||
import { createTestEditor } from '../../test_utils';
|
||||
|
||||
jest.mock('@tiptap/extension-bubble-menu');
|
||||
|
||||
describe('content_editor/components/bubble_menus/bubble_menu', () => {
|
||||
let wrapper;
|
||||
let tiptapEditor;
|
||||
const pluginKey = 'key';
|
||||
const shouldShow = jest.fn();
|
||||
const tippyOptions = { placement: 'bottom' };
|
||||
const pluginInitializationResult = {};
|
||||
|
||||
const buildEditor = () => {
|
||||
tiptapEditor = createTestEditor();
|
||||
};
|
||||
|
||||
const createWrapper = (propsData = {}) => {
|
||||
wrapper = shallowMountExtended(BubbleMenu, {
|
||||
provide: {
|
||||
tiptapEditor,
|
||||
},
|
||||
propsData: {
|
||||
pluginKey,
|
||||
shouldShow,
|
||||
tippyOptions,
|
||||
...propsData,
|
||||
},
|
||||
slots: {
|
||||
default: '<div>menu content</div>',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const setupMocks = () => {
|
||||
BubbleMenuPlugin.mockReturnValueOnce(pluginInitializationResult);
|
||||
jest.spyOn(tiptapEditor, 'registerPlugin').mockImplementationOnce(() => true);
|
||||
};
|
||||
|
||||
const invokeTippyEvent = (eventName, eventArgs) => {
|
||||
const pluginConfig = BubbleMenuPlugin.mock.calls[0][0];
|
||||
|
||||
pluginConfig.tippyOptions[eventName](eventArgs);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
buildEditor();
|
||||
setupMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('initializes BubbleMenuPlugin', async () => {
|
||||
createWrapper({});
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(BubbleMenuPlugin).toHaveBeenCalledWith({
|
||||
pluginKey,
|
||||
editor: tiptapEditor,
|
||||
shouldShow,
|
||||
element: wrapper.vm.$el,
|
||||
tippyOptions: expect.objectContaining({
|
||||
onHidden: expect.any(Function),
|
||||
onShow: expect.any(Function),
|
||||
...tippyOptions,
|
||||
}),
|
||||
});
|
||||
|
||||
expect(tiptapEditor.registerPlugin).toHaveBeenCalledWith(pluginInitializationResult);
|
||||
});
|
||||
|
||||
it('does not render default slot by default', async () => {
|
||||
createWrapper({});
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(wrapper.text()).not.toContain('menu content');
|
||||
});
|
||||
|
||||
describe('when onShow event handler is invoked', () => {
|
||||
const onShowArgs = {};
|
||||
|
||||
beforeEach(async () => {
|
||||
createWrapper({});
|
||||
|
||||
await nextTick();
|
||||
|
||||
invokeTippyEvent('onShow', onShowArgs);
|
||||
});
|
||||
|
||||
it('displays the menu content', () => {
|
||||
expect(wrapper.text()).toContain('menu content');
|
||||
});
|
||||
|
||||
it('emits show event', () => {
|
||||
expect(wrapper.emitted('show')).toEqual([[onShowArgs]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when onHidden event handler is invoked', () => {
|
||||
const onHiddenArgs = {};
|
||||
|
||||
beforeEach(async () => {
|
||||
createWrapper({});
|
||||
|
||||
await nextTick();
|
||||
|
||||
invokeTippyEvent('onShow', onHiddenArgs);
|
||||
invokeTippyEvent('onHidden', onHiddenArgs);
|
||||
});
|
||||
|
||||
it('displays the menu content', () => {
|
||||
expect(wrapper.text()).not.toContain('menu content');
|
||||
});
|
||||
|
||||
it('emits show event', () => {
|
||||
expect(wrapper.emitted('hidden')).toEqual([[onHiddenArgs]]);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,4 +1,3 @@
|
|||
import { BubbleMenu } from '@tiptap/vue-2';
|
||||
import {
|
||||
GlDropdown,
|
||||
GlDropdownForm,
|
||||
|
@ -11,6 +10,7 @@ import { mountExtended } from 'helpers/vue_test_utils_helper';
|
|||
import { stubComponent } from 'helpers/stub_component';
|
||||
import CodeBlockBubbleMenu from '~/content_editor/components/bubble_menus/code_block_bubble_menu.vue';
|
||||
import eventHubFactory from '~/helpers/event_hub_factory';
|
||||
import BubbleMenu from '~/content_editor/components/bubble_menus/bubble_menu.vue';
|
||||
import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight';
|
||||
import Diagram from '~/content_editor/extensions/diagram';
|
||||
import codeBlockLanguageLoader from '~/content_editor/services/code_block_language_loader';
|
||||
|
@ -40,6 +40,7 @@ describe('content_editor/components/bubble_menus/code_block_bubble_menu', () =>
|
|||
},
|
||||
stubs: {
|
||||
GlDropdownItem: stubComponent(GlDropdownItem),
|
||||
BubbleMenu: stubComponent(BubbleMenu),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@ -73,7 +74,6 @@ describe('content_editor/components/bubble_menus/code_block_bubble_menu', () =>
|
|||
|
||||
await emitEditorEvent({ event: 'transaction', tiptapEditor });
|
||||
|
||||
expect(bubbleMenu.props('editor')).toBe(tiptapEditor);
|
||||
expect(bubbleMenu.classes()).toEqual(['gl-shadow', 'gl-rounded-base', 'gl-bg-white']);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { BubbleMenu } from '@tiptap/vue-2';
|
||||
import { mockTracking } from 'helpers/tracking_helper';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import FormattingBubbleMenu from '~/content_editor/components/bubble_menus/formatting_bubble_menu.vue';
|
||||
import BubbleMenu from '~/content_editor/components/bubble_menus/bubble_menu.vue';
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
|
||||
import {
|
||||
BUBBLE_MENU_TRACKING_ACTION,
|
||||
|
@ -25,6 +26,9 @@ describe('content_editor/components/bubble_menus/formatting_bubble_menu', () =>
|
|||
provide: {
|
||||
tiptapEditor,
|
||||
},
|
||||
stubs: {
|
||||
BubbleMenu: stubComponent(BubbleMenu),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -41,7 +45,6 @@ describe('content_editor/components/bubble_menus/formatting_bubble_menu', () =>
|
|||
buildWrapper();
|
||||
const bubbleMenu = wrapper.findComponent(BubbleMenu);
|
||||
|
||||
expect(bubbleMenu.props().editor).toBe(tiptapEditor);
|
||||
expect(bubbleMenu.classes()).toEqual(['gl-shadow', 'gl-rounded-base', 'gl-bg-white']);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { GlLink, GlForm } from '@gitlab/ui';
|
||||
import { BubbleMenu } from '@tiptap/vue-2';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import LinkBubbleMenu from '~/content_editor/components/bubble_menus/link_bubble_menu.vue';
|
||||
import eventHubFactory from '~/helpers/event_hub_factory';
|
||||
import BubbleMenu from '~/content_editor/components/bubble_menus/bubble_menu.vue';
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
import Link from '~/content_editor/extensions/link';
|
||||
import { createTestEditor, emitEditorEvent } from '../../test_utils';
|
||||
|
||||
|
@ -28,6 +29,9 @@ describe('content_editor/components/bubble_menus/link_bubble_menu', () => {
|
|||
contentEditor,
|
||||
eventHub,
|
||||
},
|
||||
stubs: {
|
||||
BubbleMenu: stubComponent(BubbleMenu),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -60,7 +64,6 @@ describe('content_editor/components/bubble_menus/link_bubble_menu', () => {
|
|||
});
|
||||
|
||||
it('renders bubble menu component', async () => {
|
||||
expect(bubbleMenu.props('editor')).toBe(tiptapEditor);
|
||||
expect(bubbleMenu.classes()).toEqual(['gl-shadow', 'gl-rounded-base', 'gl-bg-white']);
|
||||
});
|
||||
|
||||
|
@ -157,19 +160,6 @@ describe('content_editor/components/bubble_menus/link_bubble_menu', () => {
|
|||
expect(to).toBe(18);
|
||||
});
|
||||
|
||||
it('shows the copy/edit/remove link buttons again if selection changes to another non-link and then back again to a link', async () => {
|
||||
expectLinkButtonsToExist(false);
|
||||
|
||||
tiptapEditor.commands.setTextSelection(3);
|
||||
await emitEditorEvent({ event: 'transaction', tiptapEditor });
|
||||
|
||||
tiptapEditor.commands.setTextSelection(14);
|
||||
await emitEditorEvent({ event: 'transaction', tiptapEditor });
|
||||
|
||||
expectLinkButtonsToExist(true);
|
||||
expect(wrapper.findComponent(GlForm).exists()).toBe(false);
|
||||
});
|
||||
|
||||
describe('after making changes in the form and clicking apply', () => {
|
||||
beforeEach(async () => {
|
||||
linkHrefInput.setValue('https://google.com');
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { GlLink, GlForm } from '@gitlab/ui';
|
||||
import { BubbleMenu } from '@tiptap/vue-2';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import BubbleMenu from '~/content_editor/components/bubble_menus/bubble_menu.vue';
|
||||
import MediaBubbleMenu from '~/content_editor/components/bubble_menus/media_bubble_menu.vue';
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
import eventHubFactory from '~/helpers/event_hub_factory';
|
||||
import Image from '~/content_editor/extensions/image';
|
||||
import Audio from '~/content_editor/extensions/audio';
|
||||
|
@ -54,6 +55,9 @@ describe.each`
|
|||
contentEditor,
|
||||
eventHub,
|
||||
},
|
||||
stubs: {
|
||||
BubbleMenu: stubComponent(BubbleMenu),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -94,7 +98,6 @@ describe.each`
|
|||
});
|
||||
|
||||
it('renders bubble menu component', async () => {
|
||||
expect(bubbleMenu.props('editor')).toBe(tiptapEditor);
|
||||
expect(bubbleMenu.classes()).toEqual(['gl-shadow', 'gl-rounded-base', 'gl-bg-white']);
|
||||
});
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ describe('Merge request widget rebase component', () => {
|
|||
expect(findRebaseMessageText()).toContain('Something went wrong!');
|
||||
});
|
||||
|
||||
describe('Rebase buttons with', () => {
|
||||
describe('Rebase buttons', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper(
|
||||
{
|
||||
|
@ -148,6 +148,79 @@ describe('Merge request widget rebase component', () => {
|
|||
expect(rebaseMock).toHaveBeenCalledWith({ skipCi: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe('Rebase when pipelines must succeed is enabled', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper(
|
||||
{
|
||||
mr: {
|
||||
rebaseInProgress: false,
|
||||
canPushToSourceBranch: true,
|
||||
onlyAllowMergeIfPipelineSucceeds: true,
|
||||
},
|
||||
service: {
|
||||
rebase: rebaseMock,
|
||||
poll: pollMock,
|
||||
},
|
||||
},
|
||||
mergeRequestWidgetGraphql,
|
||||
);
|
||||
});
|
||||
|
||||
it('renders only the rebase button', () => {
|
||||
expect(findRebaseWithoutCiButton().exists()).toBe(false);
|
||||
expect(findStandardRebaseButton().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('starts the rebase when clicking', async () => {
|
||||
findStandardRebaseButton().vm.$emit('click');
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(rebaseMock).toHaveBeenCalledWith({ skipCi: false });
|
||||
});
|
||||
});
|
||||
|
||||
describe('Rebase when pipelines must succeed and skipped pipelines are considered successful are enabled', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper(
|
||||
{
|
||||
mr: {
|
||||
rebaseInProgress: false,
|
||||
canPushToSourceBranch: true,
|
||||
onlyAllowMergeIfPipelineSucceeds: true,
|
||||
allowMergeOnSkippedPipeline: true,
|
||||
},
|
||||
service: {
|
||||
rebase: rebaseMock,
|
||||
poll: pollMock,
|
||||
},
|
||||
},
|
||||
mergeRequestWidgetGraphql,
|
||||
);
|
||||
});
|
||||
|
||||
it('renders both rebase buttons', () => {
|
||||
expect(findRebaseWithoutCiButton().exists()).toBe(true);
|
||||
expect(findStandardRebaseButton().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('starts the rebase when clicking', async () => {
|
||||
findStandardRebaseButton().vm.$emit('click');
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(rebaseMock).toHaveBeenCalledWith({ skipCi: false });
|
||||
});
|
||||
|
||||
it('starts the CI-skipping rebase when clicking on "Rebase without CI"', async () => {
|
||||
findRebaseWithoutCiButton().vm.$emit('click');
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(rebaseMock).toHaveBeenCalledWith({ skipCi: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('without permissions', () => {
|
||||
|
|
|
@ -8,7 +8,6 @@ const createComponent = () => mount(WorkItemInformation);
|
|||
describe('Work item information alert', () => {
|
||||
let wrapper;
|
||||
const tasksHelpPath = helpPagePath('user/tasks');
|
||||
const workItemsHelpPath = helpPagePath('development/work_items');
|
||||
|
||||
const findAlert = () => wrapper.findComponent(GlAlert);
|
||||
const findHelpLink = () => wrapper.findComponent(GlLink);
|
||||
|
@ -33,16 +32,12 @@ describe('Work item information alert', () => {
|
|||
expect(findAlert().props('variant')).toBe('tip');
|
||||
});
|
||||
|
||||
it('should have the correct text for primary button and link', () => {
|
||||
it('should have the correct text for title', () => {
|
||||
expect(findAlert().props('title')).toBe(WorkItemInformation.i18n.tasksInformationTitle);
|
||||
expect(findAlert().props('primaryButtonText')).toBe(
|
||||
WorkItemInformation.i18n.learnTasksButtonText,
|
||||
);
|
||||
expect(findAlert().props('primaryButtonLink')).toBe(tasksHelpPath);
|
||||
});
|
||||
|
||||
it('should have the correct link to work item link', () => {
|
||||
expect(findHelpLink().exists()).toBe(true);
|
||||
expect(findHelpLink().attributes('href')).toBe(workItemsHelpPath);
|
||||
expect(findHelpLink().attributes('href')).toBe(tasksHelpPath);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -347,4 +347,26 @@ RSpec.describe Gitlab::AlertManagement::Payload::Base do
|
|||
|
||||
it { is_expected.to be(true) }
|
||||
end
|
||||
|
||||
describe '#source' do
|
||||
subject { parsed_payload.source }
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
|
||||
context 'with alerting integration provided' do
|
||||
before do
|
||||
parsed_payload.integration = instance_double('::AlertManagement::HttpIntegration', name: 'INTEGRATION')
|
||||
end
|
||||
|
||||
it { is_expected.to eq('INTEGRATION') }
|
||||
end
|
||||
|
||||
context 'with monitoring tool defined in the raw payload' do
|
||||
before do
|
||||
allow(parsed_payload).to receive(:monitoring_tool).and_return('TOOL')
|
||||
end
|
||||
|
||||
it { is_expected.to eq('TOOL') }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -144,4 +144,40 @@ RSpec.describe Gitlab::AlertManagement::Payload::Generic do
|
|||
it { is_expected.to eq(value) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#resolved?' do
|
||||
subject { parsed_payload.resolved? }
|
||||
|
||||
context 'without end time' do
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
|
||||
context 'with end time' do
|
||||
let(:raw_payload) { { 'end_time' => Time.current.to_s } }
|
||||
|
||||
it { is_expected.to eq(true) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#source' do
|
||||
subject { parsed_payload.source }
|
||||
|
||||
it { is_expected.to eq('Generic Alert Endpoint') }
|
||||
|
||||
context 'with alerting integration provided' do
|
||||
before do
|
||||
parsed_payload.integration = instance_double('::AlertManagement::HttpIntegration', name: 'INTEGRATION')
|
||||
end
|
||||
|
||||
it { is_expected.to eq('INTEGRATION') }
|
||||
end
|
||||
|
||||
context 'with monitoring tool defined in the raw payload' do
|
||||
before do
|
||||
allow(parsed_payload).to receive(:monitoring_tool).and_return('TOOL')
|
||||
end
|
||||
|
||||
it { is_expected.to eq('TOOL') }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -50,8 +50,8 @@ RSpec.describe Gitlab::BackgroundMigration::RenameTaskSystemNoteToChecklistItem
|
|||
|
||||
let(:migration) do
|
||||
described_class.new(
|
||||
start_id: note1.id,
|
||||
end_id: note4.id,
|
||||
start_id: metadata1.id,
|
||||
end_id: metadata4.id,
|
||||
batch_table: :system_note_metadata,
|
||||
batch_column: :id,
|
||||
sub_batch_size: 2,
|
||||
|
|
|
@ -586,13 +586,36 @@ RSpec.describe API::Branches do
|
|||
let(:route) { "/projects/#{project_id}/repository/branches/#{branch_name}/unprotect" }
|
||||
|
||||
shared_examples_for 'repository unprotected branch' do
|
||||
it 'unprotects a single branch' do
|
||||
put api(route, current_user)
|
||||
context 'when branch is protected' do
|
||||
let!(:protected_branch) { create(:protected_branch, project: project, name: protected_branch_name) }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to match_response_schema('public_api/v4/branch')
|
||||
expect(json_response['name']).to eq(CGI.unescape(branch_name))
|
||||
expect(json_response['protected']).to eq(false)
|
||||
it 'unprotects a single branch' do
|
||||
expect_next_instance_of(::ProtectedBranches::DestroyService, project, current_user) do |instance|
|
||||
expect(instance).to receive(:execute).with(protected_branch).and_call_original
|
||||
end
|
||||
|
||||
put api(route, current_user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to match_response_schema('public_api/v4/branch')
|
||||
expect(json_response['name']).to eq(CGI.unescape(branch_name))
|
||||
expect(json_response['protected']).to eq(false)
|
||||
|
||||
expect { protected_branch.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when branch is not protected' do
|
||||
it 'returns a single branch response' do
|
||||
expect(::ProtectedBranches::DestroyService).not_to receive(:new)
|
||||
|
||||
put api(route, current_user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to match_response_schema('public_api/v4/branch')
|
||||
expect(json_response['name']).to eq(CGI.unescape(branch_name))
|
||||
expect(json_response['protected']).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when branch does not exist' do
|
||||
|
@ -637,8 +660,34 @@ RSpec.describe API::Branches do
|
|||
|
||||
context 'when authenticated', 'as a maintainer' do
|
||||
let(:current_user) { user }
|
||||
let(:protected_branch_name) { branch_name }
|
||||
|
||||
it_behaves_like 'repository unprotected branch'
|
||||
|
||||
context 'when branch contains a dot' do
|
||||
let(:branch_name) { branch_with_dot }
|
||||
|
||||
it_behaves_like 'repository unprotected branch'
|
||||
end
|
||||
|
||||
context 'when branch contains a slash' do
|
||||
let(:branch_name) { branch_with_slash }
|
||||
|
||||
it_behaves_like '404 response' do
|
||||
let(:request) { put api(route, current_user) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when branch contains an escaped slash' do
|
||||
let(:branch_name) { CGI.escape(branch_with_slash) }
|
||||
let(:protected_branch_name) { branch_with_slash }
|
||||
|
||||
it_behaves_like 'repository unprotected branch'
|
||||
end
|
||||
|
||||
context 'requesting with the escaped project full path' do
|
||||
let(:project_id) { CGI.escape(project.full_path) }
|
||||
|
||||
context "when a protected branch doesn't already exist" do
|
||||
it_behaves_like 'repository unprotected branch'
|
||||
|
||||
context 'when branch contains a dot' do
|
||||
|
@ -646,32 +695,6 @@ RSpec.describe API::Branches do
|
|||
|
||||
it_behaves_like 'repository unprotected branch'
|
||||
end
|
||||
|
||||
context 'when branch contains a slash' do
|
||||
let(:branch_name) { branch_with_slash }
|
||||
|
||||
it_behaves_like '404 response' do
|
||||
let(:request) { put api(route, current_user) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when branch contains an escaped slash' do
|
||||
let(:branch_name) { CGI.escape(branch_with_slash) }
|
||||
|
||||
it_behaves_like 'repository unprotected branch'
|
||||
end
|
||||
|
||||
context 'requesting with the escaped project full path' do
|
||||
let(:project_id) { CGI.escape(project.full_path) }
|
||||
|
||||
it_behaves_like 'repository unprotected branch'
|
||||
|
||||
context 'when branch contains a dot' do
|
||||
let(:branch_name) { branch_with_dot }
|
||||
|
||||
it_behaves_like 'repository unprotected branch'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -73,6 +73,10 @@ RSpec.shared_examples 'graphql query for searching issuables' do
|
|||
resolve_issuables(search: 'created')
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not return error if search term is not present' do
|
||||
expect(resolve_issuables).not_to be_instance_of(Gitlab::Graphql::Errors::ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with disable_anonymous_search as `false`' do
|
||||
|
|
|
@ -23,12 +23,10 @@ RSpec.shared_examples 'creates an alert management alert or errors' do
|
|||
end
|
||||
|
||||
context 'and fails to save' do
|
||||
let(:errors) { double(messages: { hosts: ['hosts array is over 255 chars'] }, '[]': [] ) }
|
||||
|
||||
before do
|
||||
allow(service).to receive(:alert).and_call_original
|
||||
allow(service).to receive_message_chain(:alert, :save).and_return(false)
|
||||
allow(service).to receive_message_chain(:alert, :errors).and_return(errors)
|
||||
allow(AlertManagement::Alert).to receive(:new).and_wrap_original do |m, **args|
|
||||
m.call(**args, hosts: ['a' * 256]) # hosts should be 255
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'alerts service responds with an error', :bad_request
|
||||
|
|
Loading…
Reference in New Issue