diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-24 21:12:18 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-24 21:12:18 +0300 |
commit | 06c57a837802f789b9276e23d7f505d95270f033 (patch) | |
tree | b45a80632d84a459b11376e1b575928c911eb045 | |
parent | 53ab147992c8e791582f625c80811fdda5ba4d5a (diff) |
Add latest changes from gitlab-org/gitlab@master
77 files changed, 836 insertions, 286 deletions
@@ -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 diff --git a/Gemfile.lock b/Gemfile.lock index 60e415febe6..d934cb407c6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -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) diff --git a/app/assets/javascripts/blob/blob_blame_link.js b/app/assets/javascripts/blob/blob_blame_link.js new file mode 100644 index 00000000000..41dfd7b82b8 --- /dev/null +++ b/app/assets/javascripts/blob/blob_blame_link.js @@ -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; diff --git a/app/assets/javascripts/blob/blob_links_tracking.js b/app/assets/javascripts/blob/blob_links_tracking.js index 9a49aa8b0fc..713cc3fad05 100644 --- a/app/assets/javascripts/blob/blob_links_tracking.js +++ b/app/assets/javascripts/blob/blob_links_tracking.js @@ -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; diff --git a/app/assets/javascripts/boards/components/board_card_move_to_position.vue b/app/assets/javascripts/boards/components/board_card_move_to_position.vue index 053dbb62b2e..a0cc3756fc4 100644 --- a/app/assets/javascripts/boards/components/board_card_move_to_position.vue +++ b/app/assets/javascripts/boards/components/board_card_move_to_position.vue @@ -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; diff --git a/app/assets/javascripts/content_editor/components/bubble_menus/bubble_menu.vue b/app/assets/javascripts/content_editor/components/bubble_menus/bubble_menu.vue new file mode 100644 index 00000000000..3891274e35e --- /dev/null +++ b/app/assets/javascripts/content_editor/components/bubble_menus/bubble_menu.vue @@ -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> diff --git a/app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue b/app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue index 6c0ac8e54d2..5e3bf5350ba 100644 --- a/app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue +++ b/app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue @@ -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> diff --git a/app/assets/javascripts/content_editor/components/bubble_menus/formatting_bubble_menu.vue b/app/assets/javascripts/content_editor/components/bubble_menus/formatting_bubble_menu.vue index 05ca7fd75c3..327b0967229 100644 --- a/app/assets/javascripts/content_editor/components/bubble_menus/formatting_bubble_menu.vue +++ b/app/assets/javascripts/content_editor/components/bubble_menus/formatting_bubble_menu.vue @@ -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')" diff --git a/app/assets/javascripts/content_editor/components/bubble_menus/link_bubble_menu.vue b/app/assets/javascripts/content_editor/components/bubble_menus/link_bubble_menu.vue index dae0bc63b5a..9f5afe5a8dc 100644 --- a/app/assets/javascripts/content_editor/components/bubble_menus/link_bubble_menu.vue +++ b/app/assets/javascripts/content_editor/components/bubble_menus/link_bubble_menu.vue @@ -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"> diff --git a/app/assets/javascripts/content_editor/components/bubble_menus/media_bubble_menu.vue b/app/assets/javascripts/content_editor/components/bubble_menus/media_bubble_menu.vue index a36a860c440..310bb1be81f 100644 --- a/app/assets/javascripts/content_editor/components/bubble_menus/media_bubble_menu.vue +++ b/app/assets/javascripts/content_editor/components/bubble_menus/media_bubble_menu.vue @@ -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"> diff --git a/app/assets/javascripts/pages/projects/init_blob.js b/app/assets/javascripts/pages/projects/init_blob.js index f7849e8d588..f37a2987685 100644 --- a/app/assets/javascripts/pages/projects/init_blob.js +++ b/app/assets/javascripts/pages/projects/init_blob.js @@ -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'); diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue index 7999b916e0f..78572f11f6f 100644 --- a/app/assets/javascripts/repository/components/blob_content_viewer.vue +++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue @@ -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'); } }); diff --git a/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue b/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue index 3d8a2cd847c..e50d4ae79c8 100644 --- a/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue +++ b/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue @@ -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`" > diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue index 6c5fc916799..6542170b7ff 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue @@ -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" diff --git a/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql b/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql index 981c667f27a..eac72ffb2f2 100644 --- a/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql +++ b/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql @@ -3,6 +3,7 @@ query getState($projectPath: ID!, $iid: String!) { id archived onlyAllowMergeIfPipelineSucceeds + allowMergeOnSkippedPipeline mergeRequest(iid: $iid) { id autoMergeEnabled diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js index 146cf7e11a7..ae2f95f4cfa 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js @@ -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'; diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue index ccc8b44942a..f471db24889 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue @@ -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) { diff --git a/app/assets/javascripts/work_items/components/work_item_information.vue b/app/assets/javascripts/work_items/components/work_item_information.vue index 2ff7ba169ea..ce75cc98a75 100644 --- a/app/assets/javascripts/work_items/components/work_item_information.vue +++ b/app/assets/javascripts/work_items/components/work_item_information.vue @@ -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 diff --git a/app/assets/stylesheets/framework/highlight.scss b/app/assets/stylesheets/framework/highlight.scss index 2b76e70fa17..ab426f388c6 100644 --- a/app/assets/stylesheets/framework/highlight.scss +++ b/app/assets/stylesheets/framework/highlight.scss @@ -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; diff --git a/app/controllers/projects/incidents_controller.rb b/app/controllers/projects/incidents_controller.rb index 36b52533e78..57892a45ff2 100644 --- a/app/controllers/projects/incidents_controller.rb +++ b/app/controllers/projects/incidents_controller.rb @@ -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 diff --git a/app/graphql/resolvers/concerns/search_arguments.rb b/app/graphql/resolvers/concerns/search_arguments.rb index 86c7140af0a..95c6dbf7497 100644 --- a/app/graphql/resolvers/concerns/search_arguments.rb +++ b/app/graphql/resolvers/concerns/search_arguments.rb @@ -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, diff --git a/app/services/alert_management/process_prometheus_alert_service.rb b/app/services/alert_management/process_prometheus_alert_service.rb index 1b377a3d367..e0594247975 100644 --- a/app/services/alert_management/process_prometheus_alert_service.rb +++ b/app/services/alert_management/process_prometheus_alert_service.rb @@ -36,10 +36,5 @@ module AlertManagement ) end end - - override :resolving_alert? - def resolving_alert? - incoming_payload.resolved? - end end end diff --git a/app/services/concerns/alert_management/alert_processing.rb b/app/services/concerns/alert_management/alert_processing.rb index 8c6c7b15d28..9fe82507edd 100644 --- a/app/services/concerns/alert_management/alert_processing.rb +++ b/app/services/concerns/alert_management/alert_processing.rb @@ -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 diff --git a/app/services/projects/alerting/notify_service.rb b/app/services/projects/alerting/notify_service.rb index 408d9dd24f2..9403c7bcfed 100644 --- a/app/services/projects/alerting/notify_service.rb +++ b/app/services/projects/alerting/notify_service.rb @@ -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 diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml index 23a17c07ea8..130e73a069f 100644 --- a/app/views/shared/_file_highlight.html.haml +++ b/app/views/shared/_file_highlight.html.haml @@ -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 diff --git a/data/deprecations/14-5-certificate-based-integration-with-kubernetes-saas.yml b/data/deprecations/14-5-certificate-based-integration-with-kubernetes-saas.yml index 774b41a4170..c0963ea33c3 100644 --- a/data/deprecations/14-5-certificate-based-integration-with-kubernetes-saas.yml +++ b/data/deprecations/14-5-certificate-based-integration-with-kubernetes-saas.yml @@ -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. diff --git a/doc/administration/load_balancer.md b/doc/administration/load_balancer.md index 869019a0d01..87b63c6272d 100644 --- a/doc/administration/load_balancer.md +++ b/doc/administration/load_balancer.md @@ -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. diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md index c2ab34cc6b8..69c00b42d35 100644 --- a/doc/administration/packages/container_registry.md +++ b/doc/administration/packages/container_registry.md @@ -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`. diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md index 992757cfc1c..e9d7b5087c5 100644 --- a/doc/administration/pages/index.md +++ b/doc/administration/pages/index.md @@ -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`: diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md index a5075336101..268a81afd5d 100644 --- a/doc/administration/reference_architectures/10k_users.md +++ b/doc/administration/reference_architectures/10k_users.md @@ -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 diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md index 59ce0ce072b..575ebf61b4a 100644 --- a/doc/administration/reference_architectures/25k_users.md +++ b/doc/administration/reference_architectures/25k_users.md @@ -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 diff --git a/doc/administration/reference_architectures/2k_users.md b/doc/administration/reference_architectures/2k_users.md index a0a2f7e4b85..3ac3cf9837b 100644 --- a/doc/administration/reference_architectures/2k_users.md +++ b/doc/administration/reference_architectures/2k_users.md @@ -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 diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md index bf9b9f47c16..76f841fdd34 100644 --- a/doc/administration/reference_architectures/3k_users.md +++ b/doc/administration/reference_architectures/3k_users.md @@ -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 diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md index 9a086f173d7..83463360b1c 100644 --- a/doc/administration/reference_architectures/50k_users.md +++ b/doc/administration/reference_architectures/50k_users.md @@ -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 diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md index 5301d55300f..70cf4990837 100644 --- a/doc/administration/reference_architectures/5k_users.md +++ b/doc/administration/reference_architectures/5k_users.md @@ -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 diff --git a/doc/administration/troubleshooting/ssl.md b/doc/administration/troubleshooting/ssl.md index e1cd92a788f..98747e33151 100644 --- a/doc/administration/troubleshooting/ssl.md +++ b/doc/administration/troubleshooting/ssl.md @@ -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 diff --git a/doc/api/container_registry.md b/doc/api/container_registry.md index 0dd2106b4d1..f23641abb4f 100644 --- a/doc/api/container_registry.md +++ b/doc/api/container_registry.md @@ -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 diff --git a/doc/ci/pipelines/index.md b/doc/ci/pipelines/index.md index c76ee74f5f8..0b43eff3d2c 100644 --- a/doc/ci/pipelines/index.md +++ b/doc/ci/pipelines/index.md @@ -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 diff --git a/doc/ci/pipelines/multi_project_pipelines.md b/doc/ci/pipelines/multi_project_pipelines.md index 3fb720cfb81..1664c62f98f 100644 --- a/doc/ci/pipelines/multi_project_pipelines.md +++ b/doc/ci/pipelines/multi_project_pipelines.md @@ -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 diff --git a/doc/development/cicd/index.md b/doc/development/cicd/index.md index e8e116037de..7a2dfa01d1e 100644 --- a/doc/development/cicd/index.md +++ b/doc/development/cicd/index.md @@ -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. diff --git a/doc/development/code_review.md b/doc/development/code_review.md index 1c66464ca40..87c46a386b7 100644 --- a/doc/development/code_review.md +++ b/doc/development/code_review.md @@ -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/). diff --git a/doc/development/policies.md b/doc/development/policies.md index f0c9d0ec5f9..ddd6b8e9bce 100644 --- a/doc/development/policies.md +++ b/doc/development/policies.md @@ -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. diff --git a/doc/development/work_items.md b/doc/development/work_items.md index 0012494de1a..f15c66ae847 100644 --- a/doc/development/work_items.md +++ b/doc/development/work_items.md @@ -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. diff --git a/doc/install/aws/manual_install_aws.md b/doc/install/aws/manual_install_aws.md index 7494dc1e95e..2aa2aa0c3f7 100644 --- a/doc/install/aws/manual_install_aws.md +++ b/doc/install/aws/manual_install_aws.md @@ -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. diff --git a/doc/install/docker.md b/doc/install/docker.md index 646955e675b..bf08fecc9ca 100644 --- a/doc/install/docker.md +++ b/doc/install/docker.md @@ -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: diff --git a/doc/install/google_cloud_platform/index.md b/doc/install/google_cloud_platform/index.md index d296d4d78f4..e924b6f7c41 100644 --- a/doc/install/google_cloud_platform/index.md +++ b/doc/install/google_cloud_platform/index.md @@ -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 diff --git a/doc/integration/advanced_search/elasticsearch_troubleshooting.md b/doc/integration/advanced_search/elasticsearch_troubleshooting.md index fb558441d6a..4db8d5d7647 100644 --- a/doc/integration/advanced_search/elasticsearch_troubleshooting.md +++ b/doc/integration/advanced_search/elasticsearch_troubleshooting.md @@ -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') diff --git a/doc/integration/jira/dvcs.md b/doc/integration/jira/dvcs.md index 43a5349e0e5..d22b606fa1d 100644 --- a/doc/integration/jira/dvcs.md +++ b/doc/integration/jira/dvcs.md @@ -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 diff --git a/doc/topics/offline/quick_start_guide.md b/doc/topics/offline/quick_start_guide.md index 353ba094d1e..c00c9621756 100644 --- a/doc/topics/offline/quick_start_guide.md +++ b/doc/topics/offline/quick_start_guide.md @@ -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`: diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md index d83fe3591d5..378ea7d5d87 100644 --- a/doc/update/deprecations.md +++ b/doc/update/deprecations.md @@ -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/). diff --git a/doc/user/project/issues/issue_weight.md b/doc/user/project/issues/issue_weight.md index a2020ca7a0a..21d7fb0a764 100644 --- a/doc/user/project/issues/issue_weight.md +++ b/doc/user/project/issues/issue_weight.md @@ -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. diff --git a/doc/user/project/releases/index.md b/doc/user/project/releases/index.md index d3456e086ce..bace776c294 100644 --- a/doc/user/project/releases/index.md +++ b/doc/user/project/releases/index.md @@ -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 diff --git a/doc/user/project/releases/release_cicd_examples.md b/doc/user/project/releases/release_cicd_examples.md index f1d3e55a707..bfd83a20caf 100644 --- a/doc/user/project/releases/release_cicd_examples.md +++ b/doc/user/project/releases/release_cicd_examples.md @@ -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. diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md index b973a0f56d1..14cdc6877e0 100644 --- a/doc/user/project/settings/index.md +++ b/doc/user/project/settings/index.md @@ -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 diff --git a/doc/user/tasks.md b/doc/user/tasks.md index 6bf194d34b5..b08c0151e7b 100644 --- a/doc/user/tasks.md +++ b/doc/user/tasks.md @@ -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}**). diff --git a/lib/api/branches.rb b/lib/api/branches.rb index b8444351029..446f24683a4 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -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 diff --git a/lib/gitlab/alert_management/payload/base.rb b/lib/gitlab/alert_management/payload/base.rb index 2d769148c5f..01dcb95eab5 100644 --- a/lib/gitlab/alert_management/payload/base.rb +++ b/lib/gitlab/alert_management/payload/base.rb @@ -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 diff --git a/lib/gitlab/alert_management/payload/generic.rb b/lib/gitlab/alert_management/payload/generic.rb index 15238b5e50f..18e65779ead 100644 --- a/lib/gitlab/alert_management/payload/generic.rb +++ b/lib/gitlab/alert_management/payload/generic.rb @@ -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 diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 7c4480b8a48..244efc85cbe 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -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,13 +44551,13 @@ msgstr "" msgid "WorkItem|Undo" msgstr "" -msgid "WorkItem|Work Items" +msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}" msgstr "" -msgid "WorkItem|Work item deleted" +msgid "WorkItem|Work Items" msgstr "" -msgid "WorkItem|work items" +msgid "WorkItem|Work item deleted" msgstr "" msgid "Would you like to create a new branch?" diff --git a/package.json b/package.json index 1bc6317f00b..13f1d3ab361 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/spec/features/help_dropdown_spec.rb b/spec/features/help_dropdown_spec.rb index db98f58240d..e64c19d4708 100644 --- a/spec/features/help_dropdown_spec.rb +++ b/spec/features/help_dropdown_spec.rb @@ -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 diff --git a/spec/frontend/blob/blob_blame_link_spec.js b/spec/frontend/blob/blob_blame_link_spec.js new file mode 100644 index 00000000000..0d19177a11f --- /dev/null +++ b/spec/frontend/blob/blob_blame_link_spec.js @@ -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'); + }); +}); diff --git a/spec/frontend/blob/blob_links_tracking_spec.js b/spec/frontend/blob/blob_links_tracking_spec.js index 22e087bc180..8ef1e9f0ac9 100644 --- a/spec/frontend/blob/blob_links_tracking_spec.js +++ b/spec/frontend/blob/blob_links_tracking_spec.js @@ -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'); }); diff --git a/spec/frontend/boards/components/board_card_move_to_position_spec.js b/spec/frontend/boards/components/board_card_move_to_position_spec.js index f6c66baa6aa..01bad53d9e1 100644 --- a/spec/frontend/boards/components/board_card_move_to_position_spec.js +++ b/spec/frontend/boards/components/board_card_move_to_position_spec.js @@ -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, + }); }, ); }); diff --git a/spec/frontend/content_editor/components/bubble_menus/bubble_menu_spec.js b/spec/frontend/content_editor/components/bubble_menus/bubble_menu_spec.js new file mode 100644 index 00000000000..0700cf5d529 --- /dev/null +++ b/spec/frontend/content_editor/components/bubble_menus/bubble_menu_spec.js @@ -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]]); + }); + }); +}); diff --git a/spec/frontend/content_editor/components/bubble_menus/code_block_bubble_menu_spec.js b/spec/frontend/content_editor/components/bubble_menus/code_block_bubble_menu_spec.js index f2c0ec59ccd..378b11f4ae9 100644 --- a/spec/frontend/content_editor/components/bubble_menus/code_block_bubble_menu_spec.js +++ b/spec/frontend/content_editor/components/bubble_menus/code_block_bubble_menu_spec.js @@ -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']); }); diff --git a/spec/frontend/content_editor/components/bubble_menus/formatting_bubble_menu_spec.js b/spec/frontend/content_editor/components/bubble_menus/formatting_bubble_menu_spec.js index 38ea9dc2b21..cce17176129 100644 --- a/spec/frontend/content_editor/components/bubble_menus/formatting_bubble_menu_spec.js +++ b/spec/frontend/content_editor/components/bubble_menus/formatting_bubble_menu_spec.js @@ -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']); }); diff --git a/spec/frontend/content_editor/components/bubble_menus/link_bubble_menu_spec.js b/spec/frontend/content_editor/components/bubble_menus/link_bubble_menu_spec.js index 04bf4efb7e2..7f8c770e503 100644 --- a/spec/frontend/content_editor/components/bubble_menus/link_bubble_menu_spec.js +++ b/spec/frontend/content_editor/components/bubble_menus/link_bubble_menu_spec.js @@ -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'); diff --git a/spec/frontend/content_editor/components/bubble_menus/media_bubble_menu_spec.js b/spec/frontend/content_editor/components/bubble_menus/media_bubble_menu_spec.js index 51f46389526..c34862b6f3f 100644 --- a/spec/frontend/content_editor/components/bubble_menus/media_bubble_menu_spec.js +++ b/spec/frontend/content_editor/components/bubble_menus/media_bubble_menu_spec.js @@ -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']); }); diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js index 534c0baf35d..05c259de370 100644 --- a/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js @@ -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', () => { diff --git a/spec/frontend/work_items/components/work_item_information_spec.js b/spec/frontend/work_items/components/work_item_information_spec.js index d5f6921c2bc..887c5f615e9 100644 --- a/spec/frontend/work_items/components/work_item_information_spec.js +++ b/spec/frontend/work_items/components/work_item_information_spec.js @@ -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); }); }); diff --git a/spec/lib/gitlab/alert_management/payload/base_spec.rb b/spec/lib/gitlab/alert_management/payload/base_spec.rb index ad2a3c7b462..3e8d71ac673 100644 --- a/spec/lib/gitlab/alert_management/payload/base_spec.rb +++ b/spec/lib/gitlab/alert_management/payload/base_spec.rb @@ -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 diff --git a/spec/lib/gitlab/alert_management/payload/generic_spec.rb b/spec/lib/gitlab/alert_management/payload/generic_spec.rb index 59933f7459d..bc3b6edc638 100644 --- a/spec/lib/gitlab/alert_management/payload/generic_spec.rb +++ b/spec/lib/gitlab/alert_management/payload/generic_spec.rb @@ -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 diff --git a/spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb b/spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb index 6996249cb40..45932defaf9 100644 --- a/spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb +++ b/spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb @@ -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, diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index cc696d76a02..2c0f6943243 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -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,40 +660,40 @@ RSpec.describe API::Branches do context 'when authenticated', 'as a maintainer' do let(:current_user) { user } + let(:protected_branch_name) { branch_name } - context "when a protected branch doesn't already exist" do - it_behaves_like 'repository unprotected branch' + it_behaves_like 'repository unprotected branch' - context 'when branch contains a dot' do - let(:branch_name) { branch_with_dot } + context 'when branch contains a dot' do + let(:branch_name) { branch_with_dot } - it_behaves_like 'repository unprotected branch' - end + it_behaves_like 'repository unprotected branch' + end - context 'when branch contains a slash' do - let(:branch_name) { branch_with_slash } + 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 + 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) } + 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 + 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 'requesting with the escaped project full path' do + let(:project_id) { CGI.escape(project.full_path) } - it_behaves_like 'repository unprotected branch' + it_behaves_like 'repository unprotected branch' - context 'when branch contains a dot' do - let(:branch_name) { branch_with_dot } + context 'when branch contains a dot' do + let(:branch_name) { branch_with_dot } - it_behaves_like 'repository unprotected branch' - end + it_behaves_like 'repository unprotected branch' end end end diff --git a/spec/support/shared_examples/graphql/resolvers/issuable_resolvers_shared_examples.rb b/spec/support/shared_examples/graphql/resolvers/issuable_resolvers_shared_examples.rb index feaa8070090..25008bca619 100644 --- a/spec/support/shared_examples/graphql/resolvers/issuable_resolvers_shared_examples.rb +++ b/spec/support/shared_examples/graphql/resolvers/issuable_resolvers_shared_examples.rb @@ -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 diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb index 9546b6cbea4..0db9519f760 100644 --- a/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb +++ b/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb @@ -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 |