summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-05-16 21:09:09 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-05-16 21:09:09 +0000
commitcffe2c2c348d86d67298fa6516d49c36d696ab2d (patch)
tree9511c62edd677012325946d19e4d2df4a9733aa1
parent7ff36fc6e9f0f5dffb41bfc79b4f07b2ce93e1c7 (diff)
downloadgitlab-ce-cffe2c2c348d86d67298fa6516d49c36d696ab2d.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/flash.js85
-rw-r--r--app/assets/javascripts/main.js6
-rw-r--r--app/assets/javascripts/merge_request_tabs.js47
-rw-r--r--app/assets/javascripts/right_sidebar.js27
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignee_title.vue3
-rw-r--r--app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/participants/participants.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/reviewers/uncollapsed_reviewer_list.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue18
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue2
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss52
-rw-r--r--app/assets/stylesheets/page_bundles/merge_requests.scss8
-rw-r--r--app/assets/stylesheets/pages/issuable.scss73
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss3
-rw-r--r--app/controllers/projects/merge_requests_controller.rb1
-rw-r--r--app/finders/alert_management/alerts_finder.rb2
-rw-r--r--app/graphql/types/ci/runner_type.rb2
-rw-r--r--app/helpers/issuables_helper.rb2
-rw-r--r--app/helpers/merge_requests_helper.rb4
-rw-r--r--app/helpers/nav_helper.rb6
-rw-r--r--app/models/alert_management/alert.rb1
-rw-r--r--app/models/clusters/applications/prometheus.rb1
-rw-r--r--app/models/clusters/cluster.rb1
-rw-r--r--app/models/deploy_token.rb6
-rw-r--r--app/models/integrations/prometheus.rb1
-rw-r--r--app/services/clusters/kubernetes.rb2
-rw-r--r--app/services/clusters/kubernetes/create_or_update_service_account_service.rb32
-rw-r--r--app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml4
-rw-r--r--app/views/projects/merge_requests/_mr_title.html.haml2
-rw-r--r--app/views/projects/merge_requests/show.html.haml12
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml13
-rw-r--r--config/feature_flags/development/moved_mr_sidebar.yml8
-rw-r--r--doc/api/graphql/reference/index.md1
-rw-r--r--doc/development/documentation/styleguide/word_list.md5
-rw-r--r--doc/topics/autodevops/customize.md2
-rw-r--r--lib/gitlab/alert_management/payload.rb5
-rw-r--r--lib/gitlab/ci/badge/coverage/template.rb4
-rw-r--r--lib/gitlab/ci/badge/pipeline/template.rb4
-rw-r--r--lib/gitlab/ci/badge/release/template.rb4
-rw-r--r--lib/gitlab/ci/badge/template.rb3
-rw-r--r--lib/gitlab/query_limiting/transaction.rb2
-rw-r--r--locale/gitlab.pot6
-rw-r--r--qa/lib/gitlab/page/group/settings/billing.rb27
-rw-r--r--qa/lib/gitlab/page/group/settings/billing.stub.rb216
-rw-r--r--qa/qa/resource/members.rb3
-rw-r--r--qa/qa/service/cluster_provider/k3s_cilium.rb93
-rw-r--r--qa/qa/specs/features/api/5_package/container_registry_spec.rb1
-rw-r--r--spec/factories/alert_management/alerts.rb14
-rw-r--r--spec/factories/deploy_tokens.rb1
-rw-r--r--spec/features/groups/issues_spec.rb65
-rw-r--r--spec/features/labels_hierarchy_spec.rb64
-rw-r--r--spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb1
-rw-r--r--spec/features/merge_request/user_manages_subscription_spec.rb45
-rw-r--r--spec/features/merge_request/user_views_open_merge_request_spec.rb2
-rw-r--r--spec/fixtures/security_reports/master/gl-common-scanning-report.json700
-rw-r--r--spec/frontend/behaviors/shortcuts/shortcuts_issuable_spec.js38
-rw-r--r--spec/graphql/resolvers/alert_management/alert_resolver_spec.rb4
-rw-r--r--spec/graphql/types/ci/runner_type_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/parsers/security/common_spec.rb29
-rw-r--r--spec/lib/gitlab/query_limiting/transaction_spec.rb6
-rw-r--r--spec/models/deploy_token_spec.rb4
-rw-r--r--spec/requests/api/graphql/ci/runner_spec.rb4
-rw-r--r--spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb2
-rw-r--r--spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb43
-rw-r--r--spec/support/helpers/filtered_search_helpers.rb4
65 files changed, 1088 insertions, 750 deletions
diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/flash.js
index c9b6a4f9913..5a47e76d597 100644
--- a/app/assets/javascripts/flash.js
+++ b/app/assets/javascripts/flash.js
@@ -86,48 +86,43 @@ const addDismissFlashClickListener = (flashEl, fadeTransition) => {
/**
* Render an alert at the top of the page, or, optionally an
- * arbitrary existing container.
- *
- * This alert is always dismissible.
- *
- * Usage:
- *
- * 1. Render a new alert
+ * arbitrary existing container. This alert is always dismissible.
*
+ * @example
+ * // Render a new alert
* import { createAlert, VARIANT_WARNING } from '~/flash';
*
* createAlert({ message: 'My error message' });
* createAlert({ message: 'My warning message', variant: VARIANT_WARNING });
*
- * 2. Dismiss this alert programmatically
- *
+ * @example
+ * // Dismiss this alert programmatically
* const alert = createAlert({ message: 'Message' });
*
* // ...
*
* alert.dismiss();
*
- * 3. Respond to the alert being dismissed
- *
- * createAlert({ message: 'Message', onDismiss: () => { ... }});
+ * @example
+ * // Respond to the alert being dismissed
+ * createAlert({ message: 'Message', onDismiss: () => {} });
*
- * @param {Object} options Options to control the flash message
- * @param {String} options.message Alert message text
- * @param {String?} options.variant Which GlAlert variant to use, should be VARIANT_SUCCESS, VARIANT_WARNING, VARIANT_DANGER, VARIANT_INFO or VARIANT_TIP. Defaults to VARIANT_DANGER.
- * @param {Object?} options.parent Reference to parent element under which alert needs to appear. Defaults to `document`.
- * @param {Function?} options.onDismiss Handler to call when this alert is dismissed.
- * @param {Object?} options.containerSelector Selector for the container of the alert
- * @param {Object?} options.primaryButton Object describing primary button of alert
- * @param {String?} link Href of primary button
- * @param {String?} text Text of primary button
- * @param {Function?} clickHandler Handler to call when primary button is clicked on. The click event is sent as an argument.
- * @param {Object?} options.secondaryButton Object describing secondary button of alert
- * @param {String?} link Href of secondary button
- * @param {String?} text Text of secondary button
- * @param {Function?} clickHandler Handler to call when secondary button is clicked on. The click event is sent as an argument.
- * @param {Boolean?} options.captureError Whether to send error to Sentry
- * @param {Object} options.error Error to be captured in Sentry
- * @returns
+ * @param {object} options - Options to control the flash message
+ * @param {string} options.message - Alert message text
+ * @param {VARIANT_SUCCESS|VARIANT_WARNING|VARIANT_DANGER|VARIANT_INFO|VARIANT_TIP} [options.variant] - Which GlAlert variant to use; it defaults to VARIANT_DANGER.
+ * @param {object} [options.parent] - Reference to parent element under which alert needs to appear. Defaults to `document`.
+ * @param {Function} [options.onDismiss] - Handler to call when this alert is dismissed.
+ * @param {string} [options.containerSelector] - Selector for the container of the alert
+ * @param {object} [options.primaryButton] - Object describing primary button of alert
+ * @param {string} [options.primaryButton.link] - Href of primary button
+ * @param {string} [options.primaryButton.text] - Text of primary button
+ * @param {Function} [options.primaryButton.clickHandler] - Handler to call when primary button is clicked on. The click event is sent as an argument.
+ * @param {object} [options.secondaryButton] - Object describing secondary button of alert
+ * @param {string} [options.secondaryButton.link] - Href of secondary button
+ * @param {string} [options.secondaryButton.text] - Text of secondary button
+ * @param {Function} [options.secondaryButton.clickHandler] - Handler to call when secondary button is clicked on. The click event is sent as an argument.
+ * @param {boolean} [options.captureError] - Whether to send error to Sentry
+ * @param {object} [options.error] - Error to be captured in Sentry
*/
const createAlert = function createAlert({
message,
@@ -208,22 +203,24 @@ const createAlert = function createAlert({
};
/**
- * Flash banner supports different types of Flash configurations
- * along with ability to provide actionConfig which can be used to show
- * additional action or link on banner next to message
+ * @deprecated use `createAlert` instead
+ *
+ * Flash banner supports different types of Flash configurations
+ * along with ability to provide actionConfig which can be used to show
+ * additional action or link on banner next to message
*
- * @param {Object} options Options to control the flash message
- * @param {String} options.message Flash message text
- * @param {String} options.type Type of Flash, it can be `notice`, `success`, `warning` or `alert` (default)
- * @param {Object} options.parent Reference to parent element under which Flash needs to appear
- * @param {Object} options.actionConfig Map of config to show action on banner
- * @param {String} href URL to which action config should point to (default: '#')
- * @param {String} title Title of action
- * @param {Function} clickHandler Method to call when action is clicked on
- * @param {Boolean} options.fadeTransition Boolean to determine whether to fade the alert out
- * @param {Boolean} options.captureError Boolean to determine whether to send error to Sentry
- * @param {Object} options.error Error to be captured in Sentry
- * @deprecated Use `createAlert` instead. See https://gitlab.com/gitlab-org/gitlab/-/issues/362334.
+ * @param {object} options - Options to control the flash message
+ * @param {string} options.message - Flash message text
+ * @param {'alert'|'notice'|'success'|'warning'} [options.type] - Type of Flash; it defaults to 'alert'
+ * @param {Element|Document} [options.parent] - Reference to parent element under which Flash needs to appear
+ * @param {object} [options.actionConfig] - Map of config to show action on banner
+ * @param {string} [options.actionConfig.href] - URL to which action config should point to (default: '#')
+ * @param {string} [options.actionConfig.title] - Title of action
+ * @param {Function} [options.actionConfig.clickHandler] - Method to call when action is clicked on
+ * @param {boolean} [options.fadeTransition] - Boolean to determine whether to fade the alert out
+ * @param {boolean} [options.addBodyClass] - Adds `flash-shown` class to the `body` element
+ * @param {boolean} [options.captureError] - Boolean to determine whether to send error to Sentry
+ * @param {object} [options.error] - Error to be captured in Sentry
*/
const createFlash = function createFlash({
message,
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 4101b520e80..2f3cdc525a7 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -195,7 +195,11 @@ $body.on('click', 'a[href^="#"]', function clickHashLinkCallback() {
* Quick fix: Get rid of jQuery for this implementation
*/
const isBoardsPage = /(projects|groups):boards:show/.test(document.body.dataset.page);
-if (!isBoardsPage && (bootstrapBreakpoint === 'sm' || bootstrapBreakpoint === 'xs')) {
+if (
+ !isBoardsPage &&
+ !window.gon?.features?.movedMrSidebar &&
+ (bootstrapBreakpoint === 'sm' || bootstrapBreakpoint === 'xs')
+) {
const $rightSidebar = $('aside.right-sidebar');
const $layoutPage = $('.layout-page');
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 61f7a079d77..e02109d1fd1 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -1,5 +1,4 @@
/* eslint-disable no-new, class-methods-use-this */
-import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import $ from 'jquery';
import Vue from 'vue';
import { getCookie, isMetaClick, parseBoolean, scrollToElement } from '~/lib/utils/common_utils';
@@ -176,6 +175,8 @@ export default class MergeRequestTabs {
: null;
this.navbar = document.querySelector('.navbar-gitlab');
this.peek = document.getElementById('js-peek');
+ this.sidebar = document.querySelector('.js-right-sidebar');
+ this.pageLayout = document.querySelector('.layout-page');
this.paddingTop = 16;
this.scrollPositions = {};
@@ -282,7 +283,7 @@ export default class MergeRequestTabs {
if (action === 'commits') {
this.loadCommits(href);
- this.expandView();
+ // this.hideSidebar();
this.resetViewContainer();
this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable);
} else if (action === 'new') {
@@ -301,13 +302,12 @@ export default class MergeRequestTabs {
*/
this.loadDiff(href);
}
- if (bp.getBreakpointSize() !== 'xl') {
- this.shrinkView();
- }
+ // this.hideSidebar();
this.expandViewContainer();
this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable);
this.commitsTab.classList.remove('active');
} else if (action === 'pipelines') {
+ // this.hideSidebar();
this.resetViewContainer();
this.mountPipelinesView();
} else {
@@ -320,9 +320,7 @@ export default class MergeRequestTabs {
notesTab.classList.add('active');
}
- if (bp.getBreakpointSize() !== 'xs') {
- this.expandView();
- }
+ // this.showSidebar();
this.resetViewContainer();
this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable);
}
@@ -509,19 +507,6 @@ export default class MergeRequestTabs {
}
}
- shrinkView() {
- const $gutterBtn = $('.js-sidebar-toggle:visible');
- const $expandSvg = $gutterBtn.find('.js-sidebar-expand');
-
- // Wait until listeners are set
- setTimeout(() => {
- // Only when sidebar is expanded
- if ($expandSvg.length && $expandSvg.hasClass('hidden')) {
- $gutterBtn.trigger('click', [true]);
- }
- }, 0);
- }
-
// Expand the issuable sidebar unless the user explicitly collapsed it
expandView() {
if (parseBoolean(getCookie('collapsed_gutter'))) {
@@ -538,4 +523,24 @@ export default class MergeRequestTabs {
}
}, 0);
}
+
+ hideSidebar() {
+ if (!isInVueNoteablePage() || this.cachedPageLayoutClasses) return;
+
+ this.cachedPageLayoutClasses = this.pageLayout.className;
+ this.pageLayout.classList.remove(
+ 'right-sidebar-collapsed',
+ 'right-sidebar-expanded',
+ 'page-with-icon-sidebar',
+ );
+ this.sidebar.style.width = '0px';
+ }
+
+ showSidebar() {
+ if (!isInVueNoteablePage() || !this.cachedPageLayoutClasses) return;
+
+ this.pageLayout.className = this.cachedPageLayoutClasses;
+ this.sidebar.style.width = '';
+ delete this.cachedPageLayoutClasses;
+ }
}
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index 009afe03ea6..a3abc8b8e90 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -7,6 +7,18 @@ import createFlash from './flash';
import axios from './lib/utils/axios_utils';
import { sprintf, s__, __ } from './locale';
+const updateSidebarClasses = (layoutPage, rightSidebar) => {
+ if (window.innerWidth >= 768) {
+ layoutPage.classList.remove('right-sidebar-expanded', 'right-sidebar-collapsed');
+ rightSidebar.classList.remove('right-sidebar-collapsed');
+ rightSidebar.classList.add('right-sidebar-expanded');
+ } else {
+ layoutPage.classList.add('right-sidebar-collapsed', 'is-merge-request');
+ rightSidebar.classList.add('right-sidebar-collapsed');
+ rightSidebar.classList.remove('right-sidebar-expanded');
+ }
+};
+
function Sidebar() {
this.toggleTodo = this.toggleTodo.bind(this);
this.sidebar = $('aside');
@@ -42,13 +54,22 @@ Sidebar.prototype.addEventListeners = function () {
this.sidebar.on('hiddenGlDropdown', this, this.onSidebarDropdownHidden);
$document.on('click', '.js-sidebar-toggle', this.sidebarToggleClicked);
- return $(document)
- .off('click', '.js-issuable-todo')
- .on('click', '.js-issuable-todo', this.toggleTodo);
+ $(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo);
+
+ if (window.gon?.features?.movedMrSidebar) {
+ const layoutPage = document.querySelector('.layout-page');
+ const rightSidebar = document.querySelector('.js-right-sidebar');
+
+ updateSidebarClasses(layoutPage, rightSidebar);
+ window.addEventListener('resize', () => updateSidebarClasses(layoutPage, rightSidebar));
+ }
};
Sidebar.prototype.sidebarToggleClicked = function (e, triggered) {
const $this = $(this);
+
+ if ($this.hasClass('right-sidebar-merge-requests')) return;
+
const $collapseIcon = $('.js-sidebar-collapse');
const $expandIcon = $('.js-sidebar-expand');
const $toggleContainer = $('.js-sidebar-toggle-container');
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue b/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue
index 68deb3761a3..6e18cf36690 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue
@@ -1,6 +1,7 @@
<script>
import { GlLoadingIcon, GlIcon } from '@gitlab/ui';
import { n__, __ } from '~/locale';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
name: 'AssigneeTitle',
@@ -8,6 +9,7 @@ export default {
GlLoadingIcon,
GlIcon,
},
+ mixins: [glFeatureFlagMixin()],
props: {
loading: {
type: Boolean,
@@ -63,6 +65,7 @@ export default {
v-if="showToggle"
:aria-label="__('Toggle sidebar')"
class="gutter-toggle float-right js-sidebar-toggle"
+ :class="{ 'gl-display-block gl-md-display-none!': glFeatures.movedMrSidebar }"
href="#"
role="button"
>
diff --git a/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue b/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
index 63ba5c31809..b0502202b0e 100644
--- a/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
+++ b/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
@@ -111,12 +111,6 @@ export default {
/>
<div data-testid="lock-status" class="sidebar-item-value" :class="lockStatus.class">
- <gl-icon
- :size="16"
- :name="lockStatus.icon"
- class="sidebar-item-icon"
- :class="lockStatus.iconClass"
- />
{{ lockStatus.displayText }}
</div>
</div>
diff --git a/app/assets/javascripts/sidebar/components/participants/participants.vue b/app/assets/javascripts/sidebar/components/participants/participants.vue
index fbecb21ecbd..77e41648e9b 100644
--- a/app/assets/javascripts/sidebar/components/participants/participants.vue
+++ b/app/assets/javascripts/sidebar/components/participants/participants.vue
@@ -103,7 +103,7 @@ export default {
</div>
<div
v-if="showParticipantLabel"
- class="title hide-collapsed gl-mb-2 gl-line-height-20 gl-font-weight-bold"
+ class="title hide-collapsed gl-mb-2! gl-line-height-20 gl-font-weight-bold"
>
<gl-loading-icon v-if="loading" size="sm" :inline="true" />
{{ participantLabel }}
diff --git a/app/assets/javascripts/sidebar/components/reviewers/uncollapsed_reviewer_list.vue b/app/assets/javascripts/sidebar/components/reviewers/uncollapsed_reviewer_list.vue
index ee3972ab3d5..2f58e11c00f 100644
--- a/app/assets/javascripts/sidebar/components/reviewers/uncollapsed_reviewer_list.vue
+++ b/app/assets/javascripts/sidebar/components/reviewers/uncollapsed_reviewer_list.vue
@@ -98,7 +98,7 @@ export default {
'gl-mb-3': index !== users.length - 1,
'attention-requests': glFeatures.mrAttentionRequests,
}"
- class="gl-display-grid gl-align-items-center reviewer-grid"
+ class="gl-display-grid gl-align-items-center reviewer-grid gl-mr-2"
data-testid="reviewer"
>
<reviewer-avatar-link
diff --git a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
index 7a10a9f3a4c..1bafa845665 100644
--- a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
+++ b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
@@ -5,6 +5,7 @@ import { IssuableType } from '~/issues/constants';
import { isLoggedIn } from '~/lib/utils/common_utils';
import { __, sprintf } from '~/locale';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { subscribedQueries, Tracking } from '~/sidebar/constants';
const ICON_ON = 'notifications';
@@ -25,6 +26,7 @@ export default {
GlToggle,
SidebarEditableItem,
},
+ mixins: [glFeatureFlagMixin()],
props: {
iid: {
type: String,
@@ -82,6 +84,9 @@ export default {
},
},
computed: {
+ isMergeRequest() {
+ return this.issuableType === IssuableType.MergeRequest && this.glFeatures.movedMrSidebar;
+ },
isLoading() {
return this.$apollo.queries?.subscribed?.loading || this.loading;
},
@@ -171,7 +176,20 @@ export default {
</script>
<template>
+ <li v-if="isMergeRequest" class="gl-new-dropdown-item">
+ <button type="button" class="dropdown-item" @click="toggleSubscribed">
+ <span class="gl-new-dropdown-item-text-wrapper">
+ <template v-if="subscribed">
+ {{ __('Turn off notifications') }}
+ </template>
+ <template v-else>
+ {{ __('Turn on notifications') }}
+ </template>
+ </span>
+ </button>
+ </li>
<sidebar-editable-item
+ v-else
ref="editable"
:title="$options.i18n.notifications"
:tracking="$options.tracking"
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
index e7d47634af2..057bb9f0100 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
@@ -204,7 +204,7 @@ export default {
:time-estimate-human-readable="humanTimeEstimate"
/>
<div
- class="hide-collapsed gl-line-height-20 gl-text-gray-900 gl-display-flex gl-align-items-center gl-font-weight-bold"
+ class="hide-collapsed gl-line-height-20 gl-text-gray-900 gl-display-flex gl-align-items-center gl-font-weight-bold gl-mr-3"
>
{{ __('Time tracking') }}
<gl-loading-icon v-if="isTimeTrackingInfoLoading" size="sm" class="gl-ml-2" inline />
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 87dac85d4cd..7c5ccd07913 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -19,13 +19,23 @@
.right-sidebar-collapsed {
padding-right: 0;
- @include media-breakpoint-up(sm) {
- &:not(.wiki-sidebar):not(.build-sidebar):not(.issuable-bulk-update-sidebar) .content-wrapper {
- padding-right: $gutter-collapsed-width;
+ &:not(.is-merge-request) {
+ @include media-breakpoint-up(sm) {
+ &:not(.wiki-sidebar):not(.build-sidebar):not(.issuable-bulk-update-sidebar) .content-wrapper {
+ padding-right: $gutter-collapsed-width;
+ }
+
+ .merge-request-tabs-holder.affix {
+ right: $gutter-collapsed-width;
+ }
}
+ }
- .merge-request-tabs-holder.affix {
- right: $gutter-collapsed-width;
+ &.is-merge-request {
+ @include media-breakpoint-up(md) {
+ .content-wrapper {
+ padding-right: $gutter-collapsed-width;
+ }
}
}
@@ -49,6 +59,18 @@
padding-right: 0;
z-index: $zindex-dropdown-menu;
+ &.right-sidebar-merge-requests {
+ width: 270px;
+
+ @include media-breakpoint-up(md) {
+ z-index: auto;
+ }
+
+ .shortcut-sidebar-dropdown-toggle {
+ margin-right: 0 !important;
+ }
+ }
+
@include media-breakpoint-only(sm) {
&:not(.wiki-sidebar):not(.build-sidebar):not(.issuable-bulk-update-sidebar) .content-wrapper {
padding-right: $gutter-collapsed-width;
@@ -73,10 +95,18 @@
.right-sidebar {
border-left: 1px solid $gray-50;
- .sidebar-container,
- .issuable-sidebar {
- // Add 100px so that potentially visible vertical scroll bar is hidden
- width: calc(100% + 100px);
+ &.right-sidebar-merge-requests {
+ @include media-breakpoint-up(md) {
+ border-left: 0;
+ }
+ }
+
+ &:not(.right-sidebar-merge-requests) {
+ .sidebar-container,
+ .issuable-sidebar {
+ // Add 100px so that potentially visible vertical scroll bar is hidden
+ width: calc(100% + 100px);
+ }
}
}
@@ -231,6 +261,10 @@
margin-right: -$gl-spacing-scale-2;
}
+.issuable-sidebar.is-merge-request .edit-link {
+ margin-right: 0;
+}
+
.assignee-grid {
grid-template-areas: ' attention user';
grid-template-columns: min-content 1fr;
diff --git a/app/assets/stylesheets/page_bundles/merge_requests.scss b/app/assets/stylesheets/page_bundles/merge_requests.scss
index 269ce4042fc..f04cdfba0e4 100644
--- a/app/assets/stylesheets/page_bundles/merge_requests.scss
+++ b/app/assets/stylesheets/page_bundles/merge_requests.scss
@@ -735,3 +735,11 @@ $tabs-holder-z-index: 250;
.attention-request-sidebar-popover {
z-index: 999;
}
+
+.merge-request-overview {
+ @include media-breakpoint-up(md) {
+ display: grid;
+ grid-template-columns: 1fr 270px;
+ grid-gap: 5%;
+ }
+}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index a0088e5ab56..5b8366e91bf 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -114,7 +114,7 @@
}
}
-.right-sidebar {
+@mixin right-sidebar {
position: fixed;
top: $header-height;
// Default value for CSS var must contain a unit
@@ -125,6 +125,18 @@
background-color: $white;
z-index: 200;
overflow: hidden;
+}
+
+.right-sidebar {
+ &:not(.right-sidebar-merge-requests) {
+ @include right-sidebar;
+ }
+
+ &.right-sidebar-merge-requests {
+ @include media-breakpoint-down(sm) {
+ @include right-sidebar;
+ }
+ }
@include media-breakpoint-down(sm) {
z-index: 251;
@@ -135,10 +147,6 @@
&:hover {
color: $blue-800;
-
- .avatar {
- border-color: rgba($gray-normal, 0.2);
- }
}
}
@@ -168,6 +176,21 @@
}
}
+ &.right-sidebar-merge-requests {
+ .block,
+ .sidebar-contained-width,
+ .issuable-sidebar-header {
+ width: 100%;
+ border-bottom: 0;
+ }
+
+ .block {
+ @include media-breakpoint-up(md) {
+ padding: $gl-spacing-scale-5 0;
+ }
+ }
+ }
+
.block,
.sidebar-contained-width,
.issuable-sidebar-header {
@@ -224,9 +247,20 @@
.issuable-sidebar {
height: 100%;
- overflow-y: scroll;
- overflow-x: hidden;
- -webkit-overflow-scrolling: touch;
+
+ &:not(.is-merge-request) {
+ overflow-y: scroll;
+ overflow-x: hidden;
+ -webkit-overflow-scrolling: touch;
+ }
+
+ &.is-merge-request {
+ @include media-breakpoint-down(sm) {
+ overflow-y: scroll;
+ overflow-x: hidden;
+ -webkit-overflow-scrolling: touch;
+ }
+ }
}
&.affix-top .issuable-sidebar {
@@ -234,7 +268,9 @@
}
&.right-sidebar-expanded {
- width: $gutter-width;
+ &:not(.right-sidebar-merge-requests) {
+ width: $gutter-width;
+ }
.value {
line-height: 1;
@@ -242,6 +278,12 @@
.issuable-sidebar {
padding: 0 20px;
+
+ &.is-merge-request {
+ @include media-breakpoint-up(md) {
+ padding: 0;
+ }
+ }
}
&:not(.boards-sidebar):not([data-signed-in]):not([data-always-show-toggle]) {
@@ -284,8 +326,17 @@
/* Extra small devices (phones, less than 768px) */
display: none;
/* Small devices (tablets, 768px and up) */
- @include media-breakpoint-up(sm) {
- display: block;
+
+ &:not(.right-sidebar-merge-requests) {
+ @include media-breakpoint-up(sm) {
+ display: block;
+ }
+ }
+
+ &.right-sidebar-merge-requests {
+ @include media-breakpoint-up(md) {
+ display: block;
+ }
}
width: $gutter-collapsed-width;
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index c40871c858f..0d3ed0e7c71 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -233,8 +233,7 @@ $tabs-holder-z-index: 250;
top: calc(#{$header-height} + #{$system-header-height} + #{$performance-bar-height});
}
- @include media-breakpoint-up(sm) {
- position: -webkit-sticky;
+ @include media-breakpoint-up(md) {
position: sticky;
}
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index be61232eace..458df40ece1 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -47,6 +47,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:mr_attention_requests, current_user)
push_frontend_feature_flag(:updated_mr_header, project)
push_frontend_feature_flag(:remove_diff_header_icons, project)
+ push_frontend_feature_flag(:moved_mr_sidebar, project)
end
before_action do
diff --git a/app/finders/alert_management/alerts_finder.rb b/app/finders/alert_management/alerts_finder.rb
index 1fbc1a4a258..11c0f37a79c 100644
--- a/app/finders/alert_management/alerts_finder.rb
+++ b/app/finders/alert_management/alerts_finder.rb
@@ -66,5 +66,3 @@ module AlertManagement
end
end
end
-
-AlertManagement::AlertsFinder.prepend_mod_with('AlertManagement::AlertsFinder')
diff --git a/app/graphql/types/ci/runner_type.rb b/app/graphql/types/ci/runner_type.rb
index 9c604209409..6f957d2511f 100644
--- a/app/graphql/types/ci/runner_type.rb
+++ b/app/graphql/types/ci/runner_type.rb
@@ -41,6 +41,8 @@ module Types
field :architecture_name, GraphQL::Types::String, null: true,
description: 'Architecture provided by the the runner.',
method: :architecture
+ field :maintenance_note, GraphQL::Types::String, null: true,
+ description: 'Runner\'s maintenance notes.'
field :groups, ::Types::GroupType.connection_type, null: true,
description: 'Groups the runner is associated with. For group runners only.'
field :id, ::Types::GlobalIDType[::Ci::Runner], null: false,
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 6ecc41151af..1bd27158be4 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -13,6 +13,8 @@ module IssuablesHelper
end
def sidebar_gutter_collapsed_class
+ return "right-sidebar-expanded" if moved_mr_sidebar_enabled?
+
"right-sidebar-#{sidebar_gutter_collapsed? ? 'collapsed' : 'expanded'}"
end
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index 821ea403dc9..a2730c53ea2 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -256,6 +256,10 @@ module MergeRequestsHelper
_('%{author} requested to merge %{source_branch} %{copy_button} into %{target_branch} %{created_at}').html_safe % { author: link_to_author.html_safe, source_branch: merge_request_source_branch(merge_request).html_safe, copy_button: copy_button.html_safe, target_branch: target_branch.html_safe, created_at: time_ago_with_tooltip(merge_request.created_at, html_class: 'gl-display-inline-block').html_safe }
end
+
+ def moved_mr_sidebar_enabled?
+ Feature.enabled?(:moved_mr_sidebar, @project) && defined?(@merge_request)
+ end
end
MergeRequestsHelper.prepend_mod_with('MergeRequestsHelper')
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
index d4d3fddb2cd..37cd491e19f 100644
--- a/app/helpers/nav_helper.rb
+++ b/app/helpers/nav_helper.rb
@@ -19,11 +19,13 @@ module NavHelper
end
def page_gutter_class
+ moved_sidebar_enabled = current_controller?('merge_requests') && moved_mr_sidebar_enabled?
+
if page_has_markdown?
if cookies[:collapsed_gutter] == 'true'
- %w[page-gutter right-sidebar-collapsed]
+ ["page-gutter", "#{'right-sidebar-collapsed' unless moved_sidebar_enabled}"]
else
- %w[page-gutter right-sidebar-expanded]
+ ["page-gutter", "#{'right-sidebar-expanded' unless moved_sidebar_enabled}"]
end
elsif current_path?('jobs#show')
%w[page-gutter build-sidebar right-sidebar-expanded]
diff --git a/app/models/alert_management/alert.rb b/app/models/alert_management/alert.rb
index 047a4d510f9..9f05c87018d 100644
--- a/app/models/alert_management/alert.rb
+++ b/app/models/alert_management/alert.rb
@@ -81,7 +81,6 @@ module AlertManagement
scope :search, -> (query) { fuzzy_search(query, [:title, :description, :monitoring_tool, :service]) }
scope :not_resolved, -> { without_status(:resolved) }
scope :with_prometheus_alert, -> { includes(:prometheus_alert) }
- scope :with_threat_monitoring_alerts, -> { where(domain: :threat_monitoring ) }
scope :with_operations_alerts, -> { where(domain: :operations) }
scope :order_start_time, -> (sort_order) { order(started_at: sort_order) }
diff --git a/app/models/clusters/applications/prometheus.rb b/app/models/clusters/applications/prometheus.rb
index 21f7e410843..d1e169a1f78 100644
--- a/app/models/clusters/applications/prometheus.rb
+++ b/app/models/clusters/applications/prometheus.rb
@@ -18,7 +18,6 @@ module Clusters
default_value_for :version, VERSION
scope :preload_cluster_platform, -> { preload(cluster: [:platform_kubernetes]) }
- scope :with_clusters_with_cilium, -> { joins(:cluster).merge(Clusters::Cluster.with_available_cilium) }
attr_encrypted :alert_manager_token,
mode: :per_attribute_iv,
diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb
index 87afa9f9491..014f7530357 100644
--- a/app/models/clusters/cluster.rb
+++ b/app/models/clusters/cluster.rb
@@ -137,7 +137,6 @@ module Clusters
scope :aws_installed, -> { aws_provided.joins(:provider_aws).merge(Clusters::Providers::Aws.with_status(:created)) }
scope :with_available_elasticstack, -> { joins(:application_elastic_stack).merge(::Clusters::Applications::ElasticStack.available) }
- scope :with_available_cilium, -> { joins(:application_cilium).merge(::Clusters::Applications::Cilium.available) }
scope :distinct_with_deployed_environments, -> { joins(:environments).merge(::Deployment.success).distinct }
scope :managed, -> { where(managed: true) }
diff --git a/app/models/deploy_token.rb b/app/models/deploy_token.rb
index 1dc8451dc09..3c0f7d91a03 100644
--- a/app/models/deploy_token.rb
+++ b/app/models/deploy_token.rb
@@ -5,7 +5,11 @@ class DeployToken < ApplicationRecord
include TokenAuthenticatable
include PolicyActor
include Gitlab::Utils::StrongMemoize
- add_authentication_token_field :token, encrypted: :optional
+ include IgnorableColumns
+
+ ignore_column :token, remove_with: '15.2', remove_after: '2022-07-22'
+
+ add_authentication_token_field :token, encrypted: :required
AVAILABLE_SCOPES = %i(read_repository read_registry write_registry
read_package_registry write_package_registry).freeze
diff --git a/app/models/integrations/prometheus.rb b/app/models/integrations/prometheus.rb
index d6aafe45ae9..5a30f55595b 100644
--- a/app/models/integrations/prometheus.rb
+++ b/app/models/integrations/prometheus.rb
@@ -30,7 +30,6 @@ module Integrations
after_create_commit :create_default_alerts
scope :preload_project, -> { preload(:project) }
- scope :with_clusters_with_cilium, -> { joins(project: [:clusters]).merge(Clusters::Cluster.with_available_cilium) }
def show_active_box?
false
diff --git a/app/services/clusters/kubernetes.rb b/app/services/clusters/kubernetes.rb
index ef549b56946..819ac4c8464 100644
--- a/app/services/clusters/kubernetes.rb
+++ b/app/services/clusters/kubernetes.rb
@@ -14,7 +14,5 @@ module Clusters
GITLAB_CROSSPLANE_DATABASE_ROLE_BINDING_NAME = 'gitlab-crossplane-database-rolebinding'
KNATIVE_SERVING_NAMESPACE = 'knative-serving'
ISTIO_SYSTEM_NAMESPACE = 'istio-system'
- GITLAB_CILIUM_ROLE_NAME = 'gitlab-cilium-role'
- GITLAB_CILIUM_ROLE_BINDING_NAME = 'gitlab-cilium-rolebinding'
end
end
diff --git a/app/services/clusters/kubernetes/create_or_update_service_account_service.rb b/app/services/clusters/kubernetes/create_or_update_service_account_service.rb
index ecad33fc7c0..eabc428d0d2 100644
--- a/app/services/clusters/kubernetes/create_or_update_service_account_service.rb
+++ b/app/services/clusters/kubernetes/create_or_update_service_account_service.rb
@@ -53,8 +53,6 @@ module Clusters
create_or_update_knative_serving_role_binding
create_or_update_crossplane_database_role
create_or_update_crossplane_database_role_binding
- create_or_update_cilium_role
- create_or_update_cilium_role_binding
end
private
@@ -99,14 +97,6 @@ module Clusters
kubeclient.update_role_binding(crossplane_database_role_binding_resource)
end
- def create_or_update_cilium_role
- kubeclient.update_role(cilium_role_resource)
- end
-
- def create_or_update_cilium_role_binding
- kubeclient.update_role_binding(cilium_role_binding_resource)
- end
-
def service_account_resource
Gitlab::Kubernetes::ServiceAccount.new(
service_account_name,
@@ -185,28 +175,6 @@ module Clusters
service_account_name: service_account_name
).generate
end
-
- def cilium_role_resource
- Gitlab::Kubernetes::Role.new(
- name: Clusters::Kubernetes::GITLAB_CILIUM_ROLE_NAME,
- namespace: service_account_namespace,
- rules: [{
- apiGroups: %w(cilium.io),
- resources: %w(ciliumnetworkpolicies),
- verbs: %w(get list create update patch)
- }]
- ).generate
- end
-
- def cilium_role_binding_resource
- Gitlab::Kubernetes::RoleBinding.new(
- name: Clusters::Kubernetes::GITLAB_CILIUM_ROLE_BINDING_NAME,
- role_name: Clusters::Kubernetes::GITLAB_CILIUM_ROLE_NAME,
- role_kind: :Role,
- namespace: service_account_namespace,
- service_account_name: service_account_name
- ).generate
- end
end
end
end
diff --git a/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml b/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml
index df9ef28726a..d4e8893407c 100644
--- a/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml
+++ b/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml
@@ -38,3 +38,7 @@
= link_to new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request)), class: 'dropdown-item' do
.gl-new-dropdown-item-text-wrapper
= _('Report abuse')
+ - if current_user && moved_mr_sidebar_enabled?
+ %li.gl-new-dropdown-divider
+ %hr.dropdown-divider
+ %li.gl-new-dropdown-item.js-sidebar-subscriptions-entry-point
diff --git a/app/views/projects/merge_requests/_mr_title.html.haml b/app/views/projects/merge_requests/_mr_title.html.haml
index 70b0a7dc650..638c520e210 100644
--- a/app/views/projects/merge_requests/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/_mr_title.html.haml
@@ -27,7 +27,7 @@
= issuable_meta(@merge_request, @project)
%div
- %button.gl-button.btn.btn-default.btn-icon.float-right.d-block.d-sm-none.gutter-toggle.issuable-gutter-toggle.js-sidebar-toggle{ type: 'button' }
+ %button.gl-button.btn.btn-default.btn-icon.float-right.gl-display-block.gutter-toggle.issuable-gutter-toggle.js-sidebar-toggle{ type: 'button', class: "#{'gl-md-display-none!' if moved_mr_sidebar_enabled? } #{'gl-sm-display-none!' unless moved_mr_sidebar_enabled?}" }
= sprite_icon('chevron-double-lg-left')
.detail-page-header-actions.js-issuable-actions{ class: "#{'gl-align-self-start is-merge-request' if updated_mr_header_enabled}" }
diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml
index 91f999eda36..13e5451df98 100644
--- a/app/views/projects/merge_requests/show.html.haml
+++ b/app/views/projects/merge_requests/show.html.haml
@@ -1,5 +1,6 @@
- @gfm_form = true
-- @content_class = "merge-request-container#{' limit-container-width' unless fluid_layout}"
+- unless moved_mr_sidebar_enabled?
+ - @content_class = "merge-request-container#{' limit-container-width' unless fluid_layout}"
- add_to_breadcrumbs _("Merge requests"), project_merge_requests_path(@project)
- breadcrumb_title @merge_request.to_reference
- page_title "#{@merge_request.title} (#{@merge_request.to_reference})", _("Merge requests")
@@ -47,8 +48,8 @@
#js-diff-file-finder
#js-code-navigation
= render "projects/merge_requests/tabs/pane", id: "notes", class: "notes voting_notes" do
- .row
- %section.col-md-12
+ %div{ class: "#{'merge-request-overview' if moved_mr_sidebar_enabled?}" }
+ %section
.issuable-discussion.js-vue-notes-event
- if @merge_request.description.present?
.detail-page-description
@@ -70,6 +71,8 @@
help_page_path: suggest_changes_help_path,
current_user_data: @current_user_data,
is_locked: @merge_request.discussion_locked.to_s } }
+ - if moved_mr_sidebar_enabled?
+ = render 'shared/issuable/sidebar', issuable_sidebar: @issuable_sidebar, assignees: @merge_request.assignees, reviewers: @merge_request.reviewers, source_branch: @merge_request.source_branch
= render "projects/merge_requests/tabs/pane", name: "commits", id: "commits", class: "commits" do
-# This tab is always loaded via AJAX
@@ -83,7 +86,8 @@
.loading.hide
= gl_loading_icon(size: 'lg')
-= render 'shared/issuable/sidebar', issuable_sidebar: @issuable_sidebar, assignees: @merge_request.assignees, reviewers: @merge_request.reviewers, source_branch: @merge_request.source_branch
+- unless moved_mr_sidebar_enabled?
+ = render 'shared/issuable/sidebar', issuable_sidebar: @issuable_sidebar, assignees: @merge_request.assignees, reviewers: @merge_request.reviewers, source_branch: @merge_request.source_branch
- if @merge_request.can_be_reverted?(current_user)
= render "projects/commit/change", type: 'revert', commit: @merge_request.merge_commit
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 0af9b1cf2e3..4317c77eb89 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -8,11 +8,12 @@
- add_page_startup_api_call "#{issuable_sidebar[:issuable_json_path]}?serializer=sidebar_extras"
- reviewers = local_assigns.fetch(:reviewers, nil)
- in_group_context_with_iterations = @project.group.present? && issuable_sidebar[:supports_iterations]
+- moved_sidebar_enabled = moved_mr_sidebar_enabled? && issuable_type === 'merge_request'
-%aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: signed_in }, issuable_type: issuable_type }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite', 'aria-label': issuable_type }
- .issuable-sidebar
- .issuable-sidebar-header.gl-py-3
- %a.gutter-toggle.float-right.js-sidebar-toggle.has-tooltip{ role: "button", href: "#", "aria-label" => _('Toggle sidebar'), title: sidebar_gutter_tooltip_text, data: { container: 'body', placement: 'left', boundary: 'viewport' } }
+%aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: signed_in }, issuable_type: issuable_type }, class: "#{sidebar_gutter_collapsed_class} #{'right-sidebar-merge-requests' if moved_sidebar_enabled}", 'aria-live' => 'polite', 'aria-label': issuable_type }
+ .issuable-sidebar{ class: "#{'is-merge-request' if moved_sidebar_enabled}" }
+ .issuable-sidebar-header{ class: "#{'gl-pb-2! gl-md-display-flex gl-justify-content-end' if moved_sidebar_enabled}" }
+ %a.gutter-toggle.float-right.js-sidebar-toggle.has-tooltip{ role: "button", class: "#{'gl-display-block gl-md-display-none!' if moved_sidebar_enabled}", href: "#", "aria-label" => _('Toggle sidebar'), title: sidebar_gutter_tooltip_text, data: { container: 'body', placement: 'left', boundary: 'viewport' } }
= sidebar_gutter_toggle_icon
- if signed_in
.js-issuable-todo{ data: { project_path: issuable_sidebar[:project_full_path], iid: issuable_sidebar[:iid], id: issuable_sidebar[:id] } }
@@ -76,14 +77,14 @@
%script#js-lock-issue-data{ type: "application/json" }= { is_locked: !!issuable_sidebar[:discussion_locked], is_editable: can_edit_issuable }.to_json.html_safe
#js-lock-entry-point
- - if signed_in
+ - if signed_in && !moved_sidebar_enabled
.js-sidebar-subscriptions-entry-point
.js-sidebar-participants-entry-point
.block.with-sub-blocks
#js-reference-entry-point
- - if issuable_type == 'merge_request'
+ - if issuable_type == 'merge_request' && !moved_sidebar_enabled
.sub-block.js-sidebar-source-branch
.sidebar-collapsed-icon.js-dont-change-state
= clipboard_button(text: source_branch, title: _('Copy branch name'), placement: "left", boundary: 'viewport', class: 'btn-clipboard gl-button btn-default-tertiary btn-icon btn-sm js-source-branch-copy')
diff --git a/config/feature_flags/development/moved_mr_sidebar.yml b/config/feature_flags/development/moved_mr_sidebar.yml
new file mode 100644
index 00000000000..b12d3023e3c
--- /dev/null
+++ b/config/feature_flags/development/moved_mr_sidebar.yml
@@ -0,0 +1,8 @@
+---
+name: moved_mr_sidebar
+introduced_by_url:
+rollout_issue_url:
+milestone: '14.10'
+type: development
+group: group::code review
+default_enabled: false
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index c5060028488..8d7ca8a84b1 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -9567,6 +9567,7 @@ Represents the total number of issues and their weights for a particular day.
| <a id="cirunneripaddress"></a>`ipAddress` | [`String`](#string) | IP address of the runner. |
| <a id="cirunnerjobcount"></a>`jobCount` | [`Int`](#int) | Number of jobs processed by the runner (limited to 1000, plus one to indicate that more items exist). |
| <a id="cirunnerlocked"></a>`locked` | [`Boolean`](#boolean) | Indicates the runner is locked. |
+| <a id="cirunnermaintenancenote"></a>`maintenanceNote` | [`String`](#string) | Runner's maintenance notes. |
| <a id="cirunnermaximumtimeout"></a>`maximumTimeout` | [`Int`](#int) | Maximum timeout (in seconds) for jobs processed by the runner. |
| <a id="cirunnerpaused"></a>`paused` | [`Boolean!`](#boolean) | Indicates the runner is paused and not available to run jobs. |
| <a id="cirunnerplatformname"></a>`platformName` | [`String`](#string) | Platform provided by the runner. |
diff --git a/doc/development/documentation/styleguide/word_list.md b/doc/development/documentation/styleguide/word_list.md
index 20bde16a6bd..e7d927de2cf 100644
--- a/doc/development/documentation/styleguide/word_list.md
+++ b/doc/development/documentation/styleguide/word_list.md
@@ -625,6 +625,11 @@ Use lowercase for **merge requests**. If you use **MR** as the acronym, spell it
Use lowercase for **milestones**.
+## n/a, N/A, not applicable
+
+When possible, use **not applicable**. Spelling out the phrase helps non-English speaking users and avoids
+capitalization inconsistencies.
+
## navigate
Do not use **navigate**. Use **go** instead. For example:
diff --git a/doc/topics/autodevops/customize.md b/doc/topics/autodevops/customize.md
index 4f797f5c238..e2d984dbbff 100644
--- a/doc/topics/autodevops/customize.md
+++ b/doc/topics/autodevops/customize.md
@@ -26,7 +26,7 @@ used for the build.
Specify either:
-- The CI/CD variable `BUILDPACK_URL` according to [`pack`'s specifications](https://buildpacks.io/docs/app-developer-guide/specify-buildpacks/).
+- The CI/CD variable `BUILDPACK_URL` with any of [`pack`'s URI specification formats](https://buildpacks.io/docs/app-developer-guide/specify-buildpacks/).
- A [`project.toml` project descriptor](https://buildpacks.io/docs/app-developer-guide/using-project-descriptor/) with the buildpacks you would like to include.
### Custom buildpacks with Herokuish
diff --git a/lib/gitlab/alert_management/payload.rb b/lib/gitlab/alert_management/payload.rb
index 1b67b91e839..de34a0f5d47 100644
--- a/lib/gitlab/alert_management/payload.rb
+++ b/lib/gitlab/alert_management/payload.rb
@@ -4,8 +4,7 @@ module Gitlab
module AlertManagement
module Payload
MONITORING_TOOLS = {
- prometheus: 'Prometheus',
- cilium: 'Cilium'
+ prometheus: 'Prometheus'
}.freeze
class << self
@@ -48,5 +47,3 @@ module Gitlab
end
end
end
-
-Gitlab::AlertManagement::Payload.prepend_mod_with('Gitlab::AlertManagement::Payload')
diff --git a/lib/gitlab/ci/badge/coverage/template.rb b/lib/gitlab/ci/badge/coverage/template.rb
index f12b4f2dbfb..18db4861dc9 100644
--- a/lib/gitlab/ci/badge/coverage/template.rb
+++ b/lib/gitlab/ci/badge/coverage/template.rb
@@ -23,13 +23,11 @@ module Gitlab::Ci
MIN_MEDIUM_DEFAULT = 75
def initialize(badge)
- @entity = badge.entity
@status = badge.status
- @key_text = badge.customization.dig(:key_text)
- @key_width = badge.customization.dig(:key_width)
@min_good = badge.customization.dig(:min_good)
@min_acceptable = badge.customization.dig(:min_acceptable)
@min_medium = badge.customization.dig(:min_medium)
+ super
end
def value_text
diff --git a/lib/gitlab/ci/badge/pipeline/template.rb b/lib/gitlab/ci/badge/pipeline/template.rb
index c39f96e4a34..417fff252a3 100644
--- a/lib/gitlab/ci/badge/pipeline/template.rb
+++ b/lib/gitlab/ci/badge/pipeline/template.rb
@@ -22,10 +22,8 @@ module Gitlab::Ci
}.freeze
def initialize(badge)
- @entity = badge.entity
@status = badge.status
- @key_text = badge.customization.dig(:key_text)
- @key_width = badge.customization.dig(:key_width)
+ super
end
def value_text
diff --git a/lib/gitlab/ci/badge/release/template.rb b/lib/gitlab/ci/badge/release/template.rb
index 65bff4371cf..354be6276fa 100644
--- a/lib/gitlab/ci/badge/release/template.rb
+++ b/lib/gitlab/ci/badge/release/template.rb
@@ -13,10 +13,8 @@ module Gitlab::Ci
VALUE_WIDTH_DEFAULT = 54
def initialize(badge)
- @entity = badge.entity
@tag = badge.tag || "none"
- @key_width = badge.customization.dig(:key_width)
- @key_text = badge.customization.dig(:key_text)
+ super
end
def key_text
diff --git a/lib/gitlab/ci/badge/template.rb b/lib/gitlab/ci/badge/template.rb
index d514a8577bd..b185fadc3a2 100644
--- a/lib/gitlab/ci/badge/template.rb
+++ b/lib/gitlab/ci/badge/template.rb
@@ -12,7 +12,8 @@ module Gitlab::Ci
def initialize(badge)
@entity = badge.entity
- @status = badge.status
+ @key_text = badge.customization.dig(:key_text)
+ @key_width = badge.customization.dig(:key_width)
end
def key_text
diff --git a/lib/gitlab/query_limiting/transaction.rb b/lib/gitlab/query_limiting/transaction.rb
index c2f1466d945..2e31849caaa 100644
--- a/lib/gitlab/query_limiting/transaction.rb
+++ b/lib/gitlab/query_limiting/transaction.rb
@@ -63,12 +63,14 @@ module Gitlab
GEO_NODES_LOAD = 'SELECT 1 AS one FROM "geo_nodes" LIMIT 1'
LICENSES_LOAD = 'SELECT "licenses".* FROM "licenses" ORDER BY "licenses"."id"'
+ ATTR_INTROSPECTION = %r/SELECT .*\ba.attname\b.* (FROM|JOIN) pg_attribute a/m.freeze
# queries can be safely ignored if they are amoritized in regular usage
# (i.e. only requested occasionally and otherwise cached).
def ignorable?(sql)
return true if sql&.include?(GEO_NODES_LOAD)
return true if sql&.include?(LICENSES_LOAD)
+ return true if ATTR_INTROSPECTION =~ sql
false
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index b90d5774a38..68106c97361 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -40027,9 +40027,15 @@ msgstr ""
msgid "Turn off"
msgstr ""
+msgid "Turn off notifications"
+msgstr ""
+
msgid "Turn on"
msgstr ""
+msgid "Turn on notifications"
+msgstr ""
+
msgid "Twitter"
msgstr ""
diff --git a/qa/lib/gitlab/page/group/settings/billing.rb b/qa/lib/gitlab/page/group/settings/billing.rb
index 24d327502f8..d0d73278890 100644
--- a/qa/lib/gitlab/page/group/settings/billing.rb
+++ b/qa/lib/gitlab/page/group/settings/billing.rb
@@ -5,13 +5,30 @@ module Gitlab
module Group
module Settings
class Billing < Chemlab::Page
- # TODO: Supplant with data-qa-selectors
- h4 :billing_plan_header, css: 'div.billing-plan-header h4'
-
+ h4 :billing_plan_header
link :start_your_free_trial
+ link :upgrade_to_premium
+ link :upgrade_to_ultimate
+
+ # Subscription details
+ strong :subscription_header
+ button :refresh_seats
+
+ # Usage
+ p :seats_in_subscription
+ p :seats_currently_in_use
+ link :see_seats_usage
+ p :max_seats_used
+ p :seats_owed
+
+ # Billing
+ p :subscription_start_date
+ p :subscription_end_date
- link :upgrade_to_premium, css: '[data-testid="plan-card-premium"] a.billing-cta-purchase-new'
- link :upgrade_to_ultimate, css: '[data-testid="plan-card-ultimate"] a.billing-cta-purchase-new'
+ def refresh_subscription_seats
+ refresh_seats
+ ::QA::Support::WaitForRequests.wait_for_requests
+ end
end
end
end
diff --git a/qa/lib/gitlab/page/group/settings/billing.stub.rb b/qa/lib/gitlab/page/group/settings/billing.stub.rb
index 64176af794a..c49d744d61f 100644
--- a/qa/lib/gitlab/page/group/settings/billing.stub.rb
+++ b/qa/lib/gitlab/page/group/settings/billing.stub.rb
@@ -100,6 +100,222 @@ module Gitlab
def upgrade_to_ultimate?
# This is a stub, used for indexing. The method is dynamically generated.
end
+
+ # @note Defined as +strong :subscription_header+
+ # @return [String] The text content or value of +subscription_header+
+ def subscription_header
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing.subscription_header_element).to exist
+ # end
+ # @return [Watir::Strong] The raw +Strong+ element
+ def subscription_header_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing).to be_subscription_header
+ # end
+ # @return [Boolean] true if the +subscription_header+ element is present on the page
+ def subscription_header?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @note Defined as +button :refresh_seats+
+ # Clicks +refresh_seats+
+ def refresh_seats
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing.refresh_seats_element).to exist
+ # end
+ # @return [Watir::Button] The raw +Button+ element
+ def refresh_seats_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing).to be_refresh_seats
+ # end
+ # @return [Boolean] true if the +refresh_seats+ element is present on the page
+ def refresh_seats?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @note Defined as +p :seats_in_subscription+
+ # @return [String] The text content or value of +seats_in_subscription+
+ def seats_in_subscription
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing.seats_in_subscription_element).to exist
+ # end
+ # @return [Watir::P] The raw +P+ element
+ def seats_in_subscription_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing).to be_seats_in_subscription
+ # end
+ # @return [Boolean] true if the +seats_in_subscription+ element is present on the page
+ def seats_in_subscription?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @note Defined as +p :seats_currently_in_use+
+ # @return [String] The text content or value of +seats_currently_in_use+
+ def seats_currently_in_use
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing.seats_currently_in_use_element).to exist
+ # end
+ # @return [Watir::P] The raw +P+ element
+ def seats_currently_in_use_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing).to be_seats_currently_in_use
+ # end
+ # @return [Boolean] true if the +seats_currently_in_use+ element is present on the page
+ def seats_currently_in_use?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @note Defined as +link :see_seats_usage+
+ # Clicks +see_seats_usage+
+ def see_seats_usage
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing.see_seats_usage_element).to exist
+ # end
+ # @return [Watir::Link] The raw +Link+ element
+ def see_seats_usage_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing).to be_see_seats_usage
+ # end
+ # @return [Boolean] true if the +see_seats_usage+ element is present on the page
+ def see_seats_usage?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @note Defined as +p :max_seats_used+
+ # @return [String] The text content or value of +max_seats_used+
+ def max_seats_used
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing.max_seats_used_element).to exist
+ # end
+ # @return [Watir::P] The raw +P+ element
+ def max_seats_used_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing).to be_max_seats_used
+ # end
+ # @return [Boolean] true if the +max_seats_used+ element is present on the page
+ def max_seats_used?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @note Defined as +p :seats_owed+
+ # @return [String] The text content or value of +seats_owed+
+ def seats_owed
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing.seats_owed_element).to exist
+ # end
+ # @return [Watir::P] The raw +P+ element
+ def seats_owed_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing).to be_seats_owed
+ # end
+ # @return [Boolean] true if the +seats_owed+ element is present on the page
+ def seats_owed?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @note Defined as +p :subscription_start_date+
+ # @return [String] The text content or value of +subscription_start_date+
+ def subscription_start_date
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing.subscription_start_date_element).to exist
+ # end
+ # @return [Watir::P] The raw +P+ element
+ def subscription_start_date_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing).to be_subscription_start_date
+ # end
+ # @return [Boolean] true if the +subscription_start_date+ element is present on the page
+ def subscription_start_date?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @note Defined as +p :subscription_end_date+
+ # @return [String] The text content or value of +subscription_end_date+
+ def subscription_end_date
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing.subscription_end_date_element).to exist
+ # end
+ # @return [Watir::P] The raw +P+ element
+ def subscription_end_date_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing).to be_subscription_end_date
+ # end
+ # @return [Boolean] true if the +subscription_end_date+ element is present on the page
+ def subscription_end_date?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
end
end
end
diff --git a/qa/qa/resource/members.rb b/qa/qa/resource/members.rb
index 83adb10c3a0..0061f74cec5 100644
--- a/qa/qa/resource/members.rb
+++ b/qa/qa/resource/members.rb
@@ -12,7 +12,8 @@ module QA
QA::Runtime::Logger.debug(%Q[Adding user #{user.username} to #{full_path} #{self.class.name}])
response = post Runtime::API::Request.new(api_client, api_members_path).url, { user_id: user.id, access_level: access_level }
- response.code == QA::Support::API::HTTP_STATUS_CREATED
+ break true if response.code == QA::Support::API::HTTP_STATUS_CREATED
+ break true if response.body.include?('Member already exists')
end
end
diff --git a/qa/qa/service/cluster_provider/k3s_cilium.rb b/qa/qa/service/cluster_provider/k3s_cilium.rb
deleted file mode 100644
index 5b529caa20b..00000000000
--- a/qa/qa/service/cluster_provider/k3s_cilium.rb
+++ /dev/null
@@ -1,93 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Service
- module ClusterProvider
- class K3sCilium < K3s
- def setup
- @k3s = Service::DockerRun::K3s.new.tap do |k3s|
- k3s.remove!
- k3s.cni_enabled = true
- k3s.register!
-
- shell "kubectl config set-cluster k3s --server https://#{k3s.host_name}:6443 --insecure-skip-tls-verify"
- shell 'kubectl config set-credentials default --username=node --password=some-secret'
- shell 'kubectl config set-context k3s --cluster=k3s --user=default'
- shell 'kubectl config use-context k3s'
-
- wait_for_server(k3s.host_name) do
- shell 'kubectl version'
- # install local storage
- shell 'kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml'
-
- # patch local storage
- shell %(kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}')
- shell 'kubectl create -f https://raw.githubusercontent.com/cilium/cilium/v1.8/install/kubernetes/quick-install.yaml'
-
- wait_for_namespaces do
- wait_for_cilium
- wait_for_coredns do
- shell 'kubectl create -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-0.31.0/deploy/static/provider/cloud/deploy.yaml'
- wait_for_ingress
- end
- end
- end
- end
- end
-
- private
-
- def wait_for_cilium
- QA::Runtime::Logger.info 'Waiting for Cilium pod to be initialized'
-
- 60.times do
- if service_available?('kubectl get pods --all-namespaces -l k8s-app=cilium --no-headers=true | grep -o "cilium-.*1/1"')
- return yield if block_given?
-
- return true
- end
-
- sleep 1
- QA::Runtime::Logger.info '.'
- end
-
- raise 'Cilium pod has not initialized correctly'
- end
-
- def wait_for_coredns
- QA::Runtime::Logger.info 'Waiting for CoreDNS pod to be initialized'
-
- 60.times do
- if service_available?('kubectl get pods --all-namespaces --no-headers=true | grep -o "coredns.*1/1"')
- return yield if block_given?
-
- return true
- end
-
- sleep 1
- QA::Runtime::Logger.info '.'
- end
-
- raise 'CoreDNS pod has not been initialized correctly'
- end
-
- def wait_for_ingress
- QA::Runtime::Logger.info 'Waiting for Ingress controller pod to be initialized'
-
- 60.times do
- if service_available?('kubectl get pods --all-namespaces -l app.kubernetes.io/component=controller | grep -o "ingress-nginx-controller.*1/1"')
- return yield if block_given?
-
- return true
- end
-
- sleep 1
- QA::Runtime::Logger.info '.'
- end
-
- raise 'Ingress pod has not been initialized correctly'
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/api/5_package/container_registry_spec.rb b/qa/qa/specs/features/api/5_package/container_registry_spec.rb
index ca9f137716b..d7207803d45 100644
--- a/qa/qa/specs/features/api/5_package/container_registry_spec.rb
+++ b/qa/qa/specs/features/api/5_package/container_registry_spec.rb
@@ -57,7 +57,6 @@ module QA
MEDIA_TYPE: 'application/vnd.docker.distribution.manifest.v2+json'
before_script:
- token=$(curl -u "$CI_REGISTRY_USER:$CI_REGISTRY_PASSWORD" "https://$CI_SERVER_HOST/jwt/auth?service=container_registry&scope=repository:$CI_PROJECT_PATH:pull,push,delete" | jq -r '.token')
- - echo $token
script:
- 'digest=$(curl -L -H "Authorization: Bearer $token" -H "Accept: $MEDIA_TYPE" "https://$CI_REGISTRY/v2/$CI_PROJECT_PATH/manifests/master" | jq -r ".layers[0].digest")'
- 'curl -L -X DELETE -H "Authorization: Bearer $token" -H "Accept: $MEDIA_TYPE" "https://$CI_REGISTRY/v2/$CI_PROJECT_PATH/blobs/$digest"'
diff --git a/spec/factories/alert_management/alerts.rb b/spec/factories/alert_management/alerts.rb
index 589a62a68bb..7e9e58edc1e 100644
--- a/spec/factories/alert_management/alerts.rb
+++ b/spec/factories/alert_management/alerts.rb
@@ -113,20 +113,6 @@ FactoryBot.define do
end
end
- trait :cilium do
- monitoring_tool { Gitlab::AlertManagement::Payload::MONITORING_TOOLS[:cilium] }
- payload do
- {
- annotations: {
- title: 'This is a cilium alert',
- summary: 'Summary of the alert',
- description: 'Description of the alert'
- },
- startsAt: started_at
- }.with_indifferent_access
- end
- end
-
trait :all_fields do
with_incident
with_assignee
diff --git a/spec/factories/deploy_tokens.rb b/spec/factories/deploy_tokens.rb
index b2c478fd3fe..a2116b738fd 100644
--- a/spec/factories/deploy_tokens.rb
+++ b/spec/factories/deploy_tokens.rb
@@ -2,7 +2,6 @@
FactoryBot.define do
factory :deploy_token do
- token { nil }
token_encrypted { Gitlab::CryptoHelper.aes256_gcm_encrypt(SecureRandom.hex(50)) }
sequence(:name) { |n| "PDT #{n}" }
read_repository { true }
diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb
index 6b663445124..ef3346b9763 100644
--- a/spec/features/groups/issues_spec.rb
+++ b/spec/features/groups/issues_spec.rb
@@ -11,6 +11,10 @@ RSpec.describe 'Group issues page' do
let(:project_with_issues_disabled) { create(:project, :issues_disabled, group: group) }
let(:path) { issues_group_path(group) }
+ before do
+ stub_feature_flags(vue_issues_list: true)
+ end
+
context 'with shared examples', :js do
let(:issuable) { create(:issue, project: project, title: "this is my created issuable")}
@@ -58,10 +62,10 @@ RSpec.describe 'Group issues page' do
let(:user2) { user_outside_group }
it 'filters by only group users' do
- filtered_search.set('assignee:=')
+ select_tokens 'Assignee', '='
- expect(find('#js-dropdown-assignee .filter-dropdown')).to have_content(user.name)
- expect(find('#js-dropdown-assignee .filter-dropdown')).not_to have_content(user2.name)
+ expect_suggestion(user.name)
+ expect_no_suggestion(user2.name)
end
end
end
@@ -76,23 +80,9 @@ RSpec.describe 'Group issues page' do
it 'returns all group and subgroup issues' do
visit issues_group_path(group)
- page.within('.issuable-list') do
- expect(page).to have_selector('li.issue', count: 2)
- expect(page).to have_content('root group issue')
- expect(page).to have_content('subgroup issue')
- end
- end
-
- it 'truncates issue counts if over the threshold', :clean_gitlab_redis_cache do
- allow(Rails.cache).to receive(:read).and_call_original
- allow(Rails.cache).to receive(:read).with(
- ['group', group.id, 'issues'],
- { expires_in: Gitlab::IssuablesCountForState::CACHE_EXPIRES_IN }
- ).and_return({ opened: 1050, closed: 500, all: 1550 })
-
- visit issues_group_path(group)
-
- expect(page).to have_text('Open 1.1k Closed 500 All 1.6k')
+ expect(page).to have_selector('li.issue', count: 2)
+ expect(page).to have_content('root group issue')
+ expect(page).to have_content('subgroup issue')
end
context 'when project is archived' do
@@ -115,7 +105,6 @@ RSpec.describe 'Group issues page' do
let!(:subgroup_issue) { create(:issue, project: subgroup_project) }
before do
- stub_feature_flags(vue_issues_list: true)
visit issues_group_path(group_with_no_issues)
end
@@ -135,14 +124,10 @@ RSpec.describe 'Group issues page' do
end
it 'shows projects only with issues feature enabled', :js do
- within '.empty-state' do
- click_button 'Toggle project select'
- end
+ click_button 'Toggle project select'
- page.within('.select2-results') do
- expect(page).to have_content(project.full_name)
- expect(page).not_to have_content(project_with_issues_disabled.full_name)
- end
+ expect(page).to have_button project.full_name
+ expect(page).not_to have_button project_with_issues_disabled.full_name
end
end
end
@@ -155,15 +140,15 @@ RSpec.describe 'Group issues page' do
let!(:issue3) { create(:issue, project: project, title: 'Issue #3', relative_position: 3) }
before do
+ stub_feature_flags(vue_issues_list: false)
+
sign_in(user_in_group)
end
it 'displays all issues' do
visit issues_group_path(group, sort: 'relative_position')
- page.within('.issues-list') do
- expect(page).to have_selector('li.issue', count: 3)
- end
+ expect(page).to have_selector('li.issue', count: 3)
end
it 'has manual-ordering css applied' do
@@ -218,11 +203,9 @@ RSpec.describe 'Group issues page' do
end
def check_issue_order
- page.within('.manual-ordering') do
- expect(find('.issue:nth-child(1) .title')).to have_content('Issue #2')
- expect(find('.issue:nth-child(2) .title')).to have_content('Issue #3')
- expect(find('.issue:nth-child(3) .title')).to have_content('Issue #1')
- end
+ expect(page).to have_css('.issue:nth-child(1) .title', text: 'Issue #2')
+ expect(page).to have_css('.issue:nth-child(2) .title', text: 'Issue #3')
+ expect(page).to have_css('.issue:nth-child(3) .title', text: 'Issue #1')
end
end
@@ -239,14 +222,8 @@ RSpec.describe 'Group issues page' do
end
it 'shows the pagination' do
- expect(page).to have_link 'Prev'
- expect(page).to have_link 'Next'
- end
-
- it 'first pagination item is active' do
- page.within('.gl-pagination') do
- expect(find('li.active')).to have_content('1')
- end
+ expect(page).to have_button 'Prev', disabled: true
+ expect(page).to have_button 'Next'
end
end
end
diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb
index 479199b72b7..ea888d4b254 100644
--- a/spec/features/labels_hierarchy_spec.rb
+++ b/spec/features/labels_hierarchy_spec.rb
@@ -17,6 +17,8 @@ RSpec.describe 'Labels Hierarchy', :js do
let!(:project_label_1) { create(:label, project: project_1, title: 'Label_4') }
before do
+ stub_feature_flags(vue_issues_list: true)
+
grandparent.add_owner(user)
sign_in(user)
@@ -34,8 +36,6 @@ RSpec.describe 'Labels Hierarchy', :js do
click_on 'Close'
end
- wait_for_requests
-
expect(page).to have_selector('.gl-label', text: label.title)
end
end
@@ -44,8 +44,6 @@ RSpec.describe 'Labels Hierarchy', :js do
page.within('.block.labels') do
click_on 'Edit'
- wait_for_requests
-
expect(page).not_to have_text(child_group_label.title)
end
end
@@ -54,15 +52,21 @@ RSpec.describe 'Labels Hierarchy', :js do
shared_examples 'filtering by ancestor labels for projects' do |board = false|
it 'filters by ancestor labels' do
[grandparent_group_label, parent_group_label, project_label_1].each do |label|
- select_label_on_dropdown(label.title)
-
- wait_for_requests
-
if board
+ select_label_on_dropdown(label.title)
+
expect(page).to have_selector('.board-card-title') do |card|
expect(card).to have_selector('a', text: labeled_issue.title)
end
else
+ within '[data-testid="filtered-search-input"]' do
+ click_filtered_search_bar
+ click_on 'Label'
+ click_on '= is'
+ click_on label.title
+ send_keys :enter
+ end
+
expect_issues_list_count(1)
expect(page).to have_selector('.issue-title', text: labeled_issue.title)
end
@@ -70,9 +74,11 @@ RSpec.describe 'Labels Hierarchy', :js do
end
it 'does not filter by descendant group labels' do
- filtered_search.set("label=")
-
- wait_for_requests
+ if board
+ filtered_search.set("label=")
+ else
+ select_tokens 'Label', '='
+ end
expect(page).not_to have_link child_group_label.title
end
@@ -93,11 +99,9 @@ RSpec.describe 'Labels Hierarchy', :js do
it 'filters by ancestors and current group labels' do
[grandparent_group_label, parent_group_label].each do |label|
- select_label_on_dropdown(label.title)
-
- wait_for_requests
-
if board
+ select_label_on_dropdown(label.title)
+
expect(page).to have_selector('.board-card-title') do |card|
expect(card).to have_selector('a', text: labeled_issue.title)
end
@@ -106,6 +110,14 @@ RSpec.describe 'Labels Hierarchy', :js do
expect(card).to have_selector('a', text: labeled_issue_2.title)
end
else
+ within '[data-testid="filtered-search-input"]' do
+ click_filtered_search_bar
+ click_on 'Label'
+ click_on '= is'
+ click_on label.title
+ send_keys :enter
+ end
+
expect_issues_list_count(3)
expect(page).to have_selector('.issue-title', text: labeled_issue.title)
expect(page).to have_selector('.issue-title', text: labeled_issue_2.title)
@@ -115,11 +127,9 @@ RSpec.describe 'Labels Hierarchy', :js do
end
it 'filters by descendant group labels' do
- wait_for_requests
-
- select_label_on_dropdown(group_label_3.title)
-
if board
+ select_label_on_dropdown(group_label_3.title)
+
expect(page).to have_selector('.board-card-title') do |card|
expect(card).not_to have_selector('a', text: labeled_issue_2.title)
end
@@ -128,17 +138,23 @@ RSpec.describe 'Labels Hierarchy', :js do
expect(card).to have_selector('a', text: labeled_issue_3.title)
end
else
+ select_tokens 'Label', '=', group_label_3.title, submit: true
+
expect_issues_list_count(1)
expect(page).to have_selector('.issue-title', text: labeled_issue_3.title)
end
end
it 'does not filter by descendant group project labels' do
- filtered_search.set("label=")
+ if board
+ filtered_search.set("label=")
- wait_for_requests
+ expect(page).not_to have_selector('.btn-link', text: project_label_3.title)
+ else
+ select_tokens 'Label', '='
- expect(page).not_to have_selector('.btn-link', text: project_label_3.title)
+ expect(page).not_to have_link project_label_3.title
+ end
end
end
@@ -195,9 +211,7 @@ RSpec.describe 'Labels Hierarchy', :js do
it_behaves_like 'filtering by ancestor labels for projects'
it 'does not filter by descendant group labels' do
- filtered_search.set("label=")
-
- wait_for_requests
+ select_tokens 'Label', '='
expect(page).not_to have_link child_group_label.title
end
diff --git a/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb b/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
index 7d67cde4bbb..f5b5460769e 100644
--- a/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
+++ b/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
@@ -59,7 +59,6 @@ RSpec.describe 'Batch diffs', :js do
# Confirm scrolled to correct UI element
expect(get_first_diff.find('.discussion-notes .timeline-entry li.note[id]').obscured?).to be_falsey
- expect(get_second_diff.find('.discussion-notes .timeline-entry li.note[id]').obscured?).to be_truthy
end
end
diff --git a/spec/features/merge_request/user_manages_subscription_spec.rb b/spec/features/merge_request/user_manages_subscription_spec.rb
index 3cdb22000f6..c64c761b8d1 100644
--- a/spec/features/merge_request/user_manages_subscription_spec.rb
+++ b/spec/features/merge_request/user_manages_subscription_spec.rb
@@ -6,29 +6,60 @@ RSpec.describe 'User manages subscription', :js do
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:user) { create(:user) }
+ let(:moved_mr_sidebar_enabled) { false }
before do
+ stub_feature_flags(moved_mr_sidebar: moved_mr_sidebar_enabled)
+
project.add_maintainer(user)
sign_in(user)
visit(merge_request_path(merge_request))
end
- it 'toggles subscription' do
- page.within('[data-testid="subscription-toggle"]') do
+ context 'moved sidebar flag disabled' do
+ it 'toggles subscription' do
+ page.within('[data-testid="subscription-toggle"]') do
+ wait_for_requests
+
+ expect(page).to have_css 'button:not(.is-checked)'
+ find('button:not(.is-checked)').click
+
+ wait_for_requests
+
+ expect(page).to have_css 'button.is-checked'
+ find('button.is-checked').click
+
+ wait_for_requests
+
+ expect(page).to have_css 'button:not(.is-checked)'
+ end
+ end
+ end
+
+ context 'moved sidebar flag enabled' do
+ let(:moved_mr_sidebar_enabled) { true }
+
+ it 'toggles subscription' do
wait_for_requests
- expect(page).to have_css 'button:not(.is-checked)'
- find('button:not(.is-checked)').click
+ click_button 'Toggle dropdown'
+
+ expect(page).to have_content('Turn on notifications')
+ click_button 'Turn on notifications'
wait_for_requests
- expect(page).to have_css 'button.is-checked'
- find('button.is-checked').click
+ click_button 'Toggle dropdown'
+
+ expect(page).to have_content('Turn off notifications')
+ click_button 'Turn off notifications'
wait_for_requests
- expect(page).to have_css 'button:not(.is-checked)'
+ click_button 'Toggle dropdown'
+
+ expect(page).to have_content('Turn on notifications')
end
end
end
diff --git a/spec/features/merge_request/user_views_open_merge_request_spec.rb b/spec/features/merge_request/user_views_open_merge_request_spec.rb
index fbb4847130c..1f4682b4a46 100644
--- a/spec/features/merge_request/user_views_open_merge_request_spec.rb
+++ b/spec/features/merge_request/user_views_open_merge_request_spec.rb
@@ -119,6 +119,8 @@ RSpec.describe 'User views an open merge request' do
let(:source_branch) { "&#39;&gt;&lt;iframe/srcdoc=&#39;&#39;&gt;&lt;/iframe&gt;" }
before do
+ stub_feature_flags(moved_mr_sidebar: false)
+
project.repository.create_branch(source_branch, "master")
mr = create(:merge_request, source_project: project, target_project: project, source_branch: source_branch)
diff --git a/spec/fixtures/security_reports/master/gl-common-scanning-report.json b/spec/fixtures/security_reports/master/gl-common-scanning-report.json
index 1fb00b2ff3a..787573301bb 100644
--- a/spec/fixtures/security_reports/master/gl-common-scanning-report.json
+++ b/spec/fixtures/security_reports/master/gl-common-scanning-report.json
@@ -1,300 +1,422 @@
{
- "vulnerabilities": [
- {
- "category": "dependency_scanning",
- "name": "Vulnerabilities in libxml2",
- "message": "Vulnerabilities in libxml2 in nokogiri",
- "description": "",
- "cve": "CVE-1020",
- "severity": "High",
- "solution": "Upgrade to latest version.",
- "scanner": {
- "id": "gemnasium",
- "name": "Gemnasium"
- },
- "evidence": {
- "source": {
- "id": "assert:CORS - Bad 'Origin' value",
- "name": "CORS - Bad 'Origin' value"
- },
- "summary": "The Origin header was changed to an invalid value of http://peachapisecurity.com and the response contained an Access-Control-Allow-Origin header which included this invalid Origin, indicating that the CORS configuration on the server is overly permissive.\n\n\n",
- "request": {
- "headers": [
- {
- "name": "Host",
- "value": "127.0.0.1:7777"
- }
- ],
- "method": "GET",
- "url": "http://127.0.0.1:7777/api/users",
- "body": ""
- },
- "response": {
- "headers": [
- {
- "name": "Server",
- "value": "TwistedWeb/20.3.0"
- }
- ],
- "reason_phrase": "OK",
- "status_code": 200,
- "body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
- },
- "supporting_messages": [
- {
- "name": "Origional",
- "request": {
- "headers": [
- {
- "name": "Host",
- "value": "127.0.0.1:7777"
- }
- ],
- "method": "GET",
- "url": "http://127.0.0.1:7777/api/users",
- "body": ""
- }
- },
- {
- "name": "Recorded",
- "request": {
- "headers": [
- {
- "name": "Host",
- "value": "127.0.0.1:7777"
- }
- ],
- "method": "GET",
- "url": "http://127.0.0.1:7777/api/users",
- "body": ""
- },
- "response": {
- "headers": [
- {
- "name": "Server",
- "value": "TwistedWeb/20.3.0"
- }
- ],
- "reason_phrase": "OK",
- "status_code": 200,
- "body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
- }
- }
- ]
- },
- "location": {},
- "identifiers": [
- {
- "type": "GitLab",
- "name": "Foo vulnerability",
- "value": "foo"
- }
- ],
- "links": [
- {
- "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1020"
- }
- ],
- "details": {
- "commit": {
- "name": [
- {
- "lang": "en",
- "value": "The Commit"
- }
- ],
- "description": [
- {
- "lang": "en",
- "value": "Commit where the vulnerability was identified"
- }
- ],
- "type": "commit",
- "value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
- }
- }
+ "vulnerabilities": [{
+ "category": "dependency_scanning",
+ "name": "Vulnerability for remediation testing 1",
+ "message": "This vulnerability should have ONE remediation",
+ "description": "",
+ "cve": "CVE-2137",
+ "severity": "High",
+ "solution": "Upgrade to latest version.",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
+ },
+ "location": {},
+ "identifiers": [{
+ "type": "GitLab",
+ "name": "Foo vulnerability",
+ "value": "foo"
+ }],
+ "links": [{
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2137"
+ }],
+ "details": {
+ "commit": {
+ "name": [{
+ "lang": "en",
+ "value": "The Commit"
+ }],
+ "description": [{
+ "lang": "en",
+ "value": "Commit where the vulnerability was identified"
+ }],
+ "type": "commit",
+ "value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
+ }
+ }
+ },
+ {
+ "category": "dependency_scanning",
+ "name": "Vulnerability for remediation testing 2",
+ "message": "This vulnerability should have ONE remediation",
+ "description": "",
+ "cve": "CVE-2138",
+ "severity": "High",
+ "solution": "Upgrade to latest version.",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
+ },
+ "location": {},
+ "identifiers": [{
+ "type": "GitLab",
+ "name": "Foo vulnerability",
+ "value": "foo"
+ }],
+ "links": [{
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2138"
+ }],
+ "details": {
+ "commit": {
+ "name": [{
+ "lang": "en",
+ "value": "The Commit"
+ }],
+ "description": [{
+ "lang": "en",
+ "value": "Commit where the vulnerability was identified"
+ }],
+ "type": "commit",
+ "value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
+ }
+ }
+ },
+ {
+ "category": "dependency_scanning",
+ "name": "Vulnerability for remediation testing 3",
+ "message": "Remediation for this vulnerability should remediate CVE-2140 as well",
+ "description": "",
+ "cve": "CVE-2139",
+ "severity": "High",
+ "solution": "Upgrade to latest version.",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
+ },
+ "location": {},
+ "identifiers": [{
+ "type": "GitLab",
+ "name": "Foo vulnerability",
+ "value": "foo"
+ }],
+ "links": [{
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2139"
+ }],
+ "details": {
+ "commit": {
+ "name": [{
+ "lang": "en",
+ "value": "The Commit"
+ }],
+ "description": [{
+ "lang": "en",
+ "value": "Commit where the vulnerability was identified"
+ }],
+ "type": "commit",
+ "value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
+ }
+ }
+ },
+ {
+ "category": "dependency_scanning",
+ "name": "Vulnerability for remediation testing 4",
+ "message": "Remediation for this vulnerability should remediate CVE-2139 as well",
+ "description": "",
+ "cve": "CVE-2140",
+ "severity": "High",
+ "solution": "Upgrade to latest version.",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
+ },
+ "location": {},
+ "identifiers": [{
+ "type": "GitLab",
+ "name": "Foo vulnerability",
+ "value": "foo"
+ }],
+ "links": [{
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2140"
+ }],
+ "details": {
+ "commit": {
+ "name": [{
+ "lang": "en",
+ "value": "The Commit"
+ }],
+ "description": [{
+ "lang": "en",
+ "value": "Commit where the vulnerability was identified"
+ }],
+ "type": "commit",
+ "value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
+ }
+ }
+ },
+ {
+ "category": "dependency_scanning",
+ "name": "Vulnerabilities in libxml2",
+ "message": "Vulnerabilities in libxml2 in nokogiri",
+ "description": "",
+ "cve": "CVE-1020",
+ "severity": "High",
+ "solution": "Upgrade to latest version.",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
+ },
+ "evidence": {
+ "source": {
+ "id": "assert:CORS - Bad 'Origin' value",
+ "name": "CORS - Bad 'Origin' value"
},
- {
- "id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3",
- "category": "dependency_scanning",
- "name": "Regular Expression Denial of Service",
- "message": "Regular Expression Denial of Service in debug",
- "description": "",
- "cve": "CVE-1030",
- "severity": "Unknown",
- "solution": "Upgrade to latest versions.",
- "scanner": {
- "id": "gemnasium",
- "name": "Gemnasium"
- },
- "evidence": {
- "source": {
- "id": "assert:CORS - Bad 'Origin' value",
- "name": "CORS - Bad 'Origin' value"
- },
- "summary": "The Origin header was changed to an invalid value of http://peachapisecurity.com and the response contained an Access-Control-Allow-Origin header which included this invalid Origin, indicating that the CORS configuration on the server is overly permissive.\n\n\n",
- "request": {
- "headers": [
- {
- "name": "Host",
- "value": "127.0.0.1:7777"
- }
- ],
- "method": "GET",
- "url": "http://127.0.0.1:7777/api/users",
- "body": ""
- },
- "response": {
- "headers": [
- {
- "name": "Server",
- "value": "TwistedWeb/20.3.0"
- }
- ],
- "reason_phrase": "OK",
- "status_code": 200,
- "body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
- },
- "supporting_messages": [
- {
- "name": "Origional",
- "request": {
- "headers": [
- {
- "name": "Host",
- "value": "127.0.0.1:7777"
- }
- ],
- "method": "GET",
- "url": "http://127.0.0.1:7777/api/users",
- "body": ""
- }
- },
- {
- "name": "Recorded",
- "request": {
- "headers": [
- {
- "name": "Host",
- "value": "127.0.0.1:7777"
- }
- ],
- "method": "GET",
- "url": "http://127.0.0.1:7777/api/users",
- "body": ""
- },
- "response": {
- "headers": [
- {
- "name": "Server",
- "value": "TwistedWeb/20.3.0"
- }
- ],
- "reason_phrase": "OK",
- "status_code": 200,
- "body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
- }
- }
- ]
- },
- "location": {},
- "identifiers": [
- {
- "type": "GitLab",
- "name": "Bar vulnerability",
- "value": "bar"
- }
- ],
- "links": [
- {
- "name": "CVE-1030",
- "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1030"
- }
- ]
+ "summary": "The Origin header was changed to an invalid value of http://peachapisecurity.com and the response contained an Access-Control-Allow-Origin header which included this invalid Origin, indicating that the CORS configuration on the server is overly permissive.\n\n\n",
+ "request": {
+ "headers": [{
+ "name": "Host",
+ "value": "127.0.0.1:7777"
+ }],
+ "method": "GET",
+ "url": "http://127.0.0.1:7777/api/users",
+ "body": ""
},
- {
- "category": "dependency_scanning",
- "name": "Authentication bypass via incorrect DOM traversal and canonicalization",
- "message": "Authentication bypass via incorrect DOM traversal and canonicalization in saml2-js",
- "description": "",
- "cve": "yarn/yarn.lock:saml2-js:gemnasium:9952e574-7b5b-46fa-a270-aeb694198a98",
- "severity": "Unknown",
- "solution": "Upgrade to fixed version.\r\n",
- "scanner": {
- "id": "gemnasium",
- "name": "Gemnasium"
+ "response": {
+ "headers": [{
+ "name": "Server",
+ "value": "TwistedWeb/20.3.0"
+ }],
+ "reason_phrase": "OK",
+ "status_code": 200,
+ "body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
+ },
+ "supporting_messages": [{
+ "name": "Origional",
+ "request": {
+ "headers": [{
+ "name": "Host",
+ "value": "127.0.0.1:7777"
+ }],
+ "method": "GET",
+ "url": "http://127.0.0.1:7777/api/users",
+ "body": ""
+ }
+ },
+ {
+ "name": "Recorded",
+ "request": {
+ "headers": [{
+ "name": "Host",
+ "value": "127.0.0.1:7777"
+ }],
+ "method": "GET",
+ "url": "http://127.0.0.1:7777/api/users",
+ "body": ""
},
- "location": {},
- "identifiers": [],
- "links": [
- ]
+ "response": {
+ "headers": [{
+ "name": "Server",
+ "value": "TwistedWeb/20.3.0"
+ }],
+ "reason_phrase": "OK",
+ "status_code": 200,
+ "body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
+ }
+ }
+ ]
+ },
+ "location": {},
+ "identifiers": [{
+ "type": "GitLab",
+ "name": "Foo vulnerability",
+ "value": "foo"
+ }],
+ "links": [{
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1020"
+ }],
+ "details": {
+ "commit": {
+ "name": [{
+ "lang": "en",
+ "value": "The Commit"
+ }],
+ "description": [{
+ "lang": "en",
+ "value": "Commit where the vulnerability was identified"
+ }],
+ "type": "commit",
+ "value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
}
- ],
- "remediations": [
- {
- "fixes": [
- {
- "cve": "CVE-1020"
- }
- ],
- "summary": "",
- "diff": ""
- },
- {
- "fixes": [
- {
- "cve": "CVE",
- "id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3"
- }
- ],
- "summary": "",
- "diff": ""
+ }
+ },
+ {
+ "id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3",
+ "category": "dependency_scanning",
+ "name": "Regular Expression Denial of Service",
+ "message": "Regular Expression Denial of Service in debug",
+ "description": "",
+ "cve": "CVE-1030",
+ "severity": "Unknown",
+ "solution": "Upgrade to latest versions.",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
+ },
+ "evidence": {
+ "source": {
+ "id": "assert:CORS - Bad 'Origin' value",
+ "name": "CORS - Bad 'Origin' value"
},
- {
- "fixes": [
- {
- "cve": "CVE",
- "id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3"
- }
- ],
- "summary": "",
- "diff": ""
+ "summary": "The Origin header was changed to an invalid value of http://peachapisecurity.com and the response contained an Access-Control-Allow-Origin header which included this invalid Origin, indicating that the CORS configuration on the server is overly permissive.\n\n\n",
+ "request": {
+ "headers": [{
+ "name": "Host",
+ "value": "127.0.0.1:7777"
+ }],
+ "method": "GET",
+ "url": "http://127.0.0.1:7777/api/users",
+ "body": ""
},
- {
- "fixes": [
- {
- "id": "2134",
- "cve": "CVE-1"
- }
- ],
- "summary": "",
- "diff": ""
- }
- ],
- "dependency_files": [],
- "scan": {
- "analyzer": {
- "id": "common-analyzer",
- "name": "Common Analyzer",
- "url": "https://site.com/analyzer/common",
- "version": "2.0.1",
- "vendor": {
- "name": "Common"
- }
+ "response": {
+ "headers": [{
+ "name": "Server",
+ "value": "TwistedWeb/20.3.0"
+ }],
+ "reason_phrase": "OK",
+ "status_code": 200,
+ "body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
},
- "scanner": {
- "id": "gemnasium",
- "name": "Gemnasium",
- "url": "https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium-maven",
- "vendor": {
- "name": "GitLab"
+ "supporting_messages": [{
+ "name": "Origional",
+ "request": {
+ "headers": [{
+ "name": "Host",
+ "value": "127.0.0.1:7777"
+ }],
+ "method": "GET",
+ "url": "http://127.0.0.1:7777/api/users",
+ "body": ""
+ }
+ },
+ {
+ "name": "Recorded",
+ "request": {
+ "headers": [{
+ "name": "Host",
+ "value": "127.0.0.1:7777"
+ }],
+ "method": "GET",
+ "url": "http://127.0.0.1:7777/api/users",
+ "body": ""
},
- "version": "2.18.0"
- },
- "type": "dependency_scanning",
- "start_time": "placeholder-value",
- "end_time": "placeholder-value",
- "status": "success"
+ "response": {
+ "headers": [{
+ "name": "Server",
+ "value": "TwistedWeb/20.3.0"
+ }],
+ "reason_phrase": "OK",
+ "status_code": 200,
+ "body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
+ }
+ }
+ ]
+ },
+ "location": {},
+ "identifiers": [{
+ "type": "GitLab",
+ "name": "Bar vulnerability",
+ "value": "bar"
+ }],
+ "links": [{
+ "name": "CVE-1030",
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1030"
+ }]
+ },
+ {
+ "category": "dependency_scanning",
+ "name": "Authentication bypass via incorrect DOM traversal and canonicalization",
+ "message": "Authentication bypass via incorrect DOM traversal and canonicalization in saml2-js",
+ "description": "",
+ "cve": "yarn/yarn.lock:saml2-js:gemnasium:9952e574-7b5b-46fa-a270-aeb694198a98",
+ "severity": "Unknown",
+ "solution": "Upgrade to fixed version.\r\n",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
+ },
+ "location": {},
+ "identifiers": [],
+ "links": []
+ }
+ ],
+ "remediations": [{
+ "fixes": [{
+ "cve": "CVE-2137"
+ }],
+ "summary": "this remediates CVE-2137",
+ "diff": "dG90YWxseSBsZWdpdCBkaWZm"
+ },
+ {
+ "fixes": [{
+ "cve": "CVE-2138"
+ }],
+ "summary": "this remediates CVE-2138",
+ "diff": "dG90YWxseSBsZWdpdCBkaWZm"
+ },
+ {
+ "fixes": [{
+ "cve": "CVE-2139"
+ }, {
+ "cve": "CVE-2140"
+ }],
+ "summary": "this remediates CVE-2139 and CVE-2140",
+ "diff": "dG90YWxseSBsZWdpdGltYXRlIGRpZmYsIDEwLzEwIHdvdWxkIGFwcGx5"
+ },
+ {
+ "fixes": [{
+ "cve": "CVE-1020"
+ }],
+ "summary": "",
+ "diff": ""
+ },
+ {
+ "fixes": [{
+ "cve": "CVE",
+ "id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3"
+ }],
+ "summary": "",
+ "diff": ""
+ },
+ {
+ "fixes": [{
+ "cve": "CVE",
+ "id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3"
+ }],
+ "summary": "",
+ "diff": ""
+ },
+ {
+ "fixes": [{
+ "id": "2134",
+ "cve": "CVE-1"
+ }],
+ "summary": "",
+ "diff": ""
+ }
+ ],
+ "dependency_files": [],
+ "scan": {
+ "analyzer": {
+ "id": "common-analyzer",
+ "name": "Common Analyzer",
+ "url": "https://site.com/analyzer/common",
+ "version": "2.0.1",
+ "vendor": {
+ "name": "Common"
+ }
+ },
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium",
+ "url": "https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium-maven",
+ "vendor": {
+ "name": "GitLab"
+ },
+ "version": "2.18.0"
},
- "version": "14.0.2"
+ "type": "dependency_scanning",
+ "start_time": "placeholder-value",
+ "end_time": "placeholder-value",
+ "status": "success"
+ },
+ "version": "14.0.2"
}
diff --git a/spec/frontend/behaviors/shortcuts/shortcuts_issuable_spec.js b/spec/frontend/behaviors/shortcuts/shortcuts_issuable_spec.js
index edcacb0f740..e6e587ff44b 100644
--- a/spec/frontend/behaviors/shortcuts/shortcuts_issuable_spec.js
+++ b/spec/frontend/behaviors/shortcuts/shortcuts_issuable_spec.js
@@ -1,5 +1,4 @@
import $ from 'jquery';
-import Mousetrap from 'mousetrap';
import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import waitForPromises from 'helpers/wait_for_promises';
import initCopyAsGFM, { CopyAsGFM } from '~/behaviors/markdown/copy_as_gfm';
@@ -13,7 +12,6 @@ jest.mock('~/lib/utils/common_utils', () => ({
describe('ShortcutsIssuable', () => {
const snippetShowFixtureName = 'snippets/show.html';
- const mrShowFixtureName = 'merge_requests/merge_request_of_current_user.html';
beforeAll(() => {
initCopyAsGFM();
@@ -282,40 +280,4 @@ describe('ShortcutsIssuable', () => {
});
});
});
-
- describe('copyBranchName', () => {
- let sidebarCollapsedBtn;
- let sidebarExpandedBtn;
-
- beforeEach(() => {
- loadHTMLFixture(mrShowFixtureName);
-
- window.shortcut = new ShortcutsIssuable();
-
- [sidebarCollapsedBtn, sidebarExpandedBtn] = document.querySelectorAll(
- '.js-source-branch-copy',
- );
-
- [sidebarCollapsedBtn, sidebarExpandedBtn].forEach((btn) => jest.spyOn(btn, 'click'));
- });
-
- afterEach(() => {
- delete window.shortcut;
- resetHTMLFixture();
- });
-
- describe('when the sidebar is expanded', () => {
- beforeEach(() => {
- // simulate the applied CSS styles when the
- // sidebar is expanded
- sidebarCollapsedBtn.style.display = 'none';
-
- Mousetrap.trigger('b');
- });
-
- it('clicks the "expanded" version of the copy source branch button', () => {
- expect(sidebarExpandedBtn.click).toHaveBeenCalled();
- });
- });
- });
});
diff --git a/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb b/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb
index c042f6dac19..14ebe85d80e 100644
--- a/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb
+++ b/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb
@@ -39,8 +39,8 @@ RSpec.describe Resolvers::AlertManagement::AlertResolver do
end
context 'filtering by domain' do
- let_it_be(:alert1) { create(:alert_management_alert, project: project, monitoring_tool: 'Cilium', domain: :threat_monitoring) }
- let_it_be(:alert2) { create(:alert_management_alert, project: project, monitoring_tool: 'Cilium', domain: :threat_monitoring) }
+ let_it_be(:alert1) { create(:alert_management_alert, project: project, monitoring_tool: 'other', domain: :threat_monitoring) }
+ let_it_be(:alert2) { create(:alert_management_alert, project: project, monitoring_tool: 'other', domain: :threat_monitoring) }
let_it_be(:alert3) { create(:alert_management_alert, project: project, monitoring_tool: 'generic') }
let(:args) { { domain: 'operations' } }
diff --git a/spec/graphql/types/ci/runner_type_spec.rb b/spec/graphql/types/ci/runner_type_spec.rb
index b2fbfda898e..26ac7a4da8d 100644
--- a/spec/graphql/types/ci/runner_type_spec.rb
+++ b/spec/graphql/types/ci/runner_type_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe GitlabSchema.types['CiRunner'] do
id description created_at contacted_at maximum_timeout access_level active paused status
version short_sha revision locked run_untagged ip_address runner_type tag_list
project_count job_count admin_url edit_admin_url user_permissions executor_name architecture_name platform_name
- groups projects jobs token_expires_at
+ maintenance_note groups projects jobs token_expires_at
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/lib/gitlab/ci/parsers/security/common_spec.rb b/spec/lib/gitlab/ci/parsers/security/common_spec.rb
index dfc5dec1481..6495d1f654b 100644
--- a/spec/lib/gitlab/ci/parsers/security/common_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/security/common_spec.rb
@@ -292,7 +292,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
expect(scans.map(&:status).all?('success')).to be(true)
expect(scans.map(&:start_time).all?('placeholder-value')).to be(true)
expect(scans.map(&:end_time).all?('placeholder-value')).to be(true)
- expect(scans.size).to eq(3)
+ expect(scans.size).to eq(7)
expect(scans.first).to be_a(::Gitlab::Ci::Reports::Security::Scan)
end
@@ -348,22 +348,29 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
it 'returns links object for each finding', :aggregate_failures do
links = report.findings.flat_map(&:links)
- expect(links.map(&:url)).to match_array(['https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1020', 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1030'])
- expect(links.map(&:name)).to match_array([nil, 'CVE-1030'])
- expect(links.size).to eq(2)
+ expect(links.map(&:url)).to match_array(['https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1020', 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1030',
+ "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2137", "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2138",
+ "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2139", "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2140"])
+ expect(links.map(&:name)).to match_array([nil, nil, nil, nil, nil, 'CVE-1030'])
+ expect(links.size).to eq(6)
expect(links.first).to be_a(::Gitlab::Ci::Reports::Security::Link)
end
end
describe 'parsing evidence' do
- it 'returns evidence object for each finding', :aggregate_failures do
- evidences = report.findings.map(&:evidence)
+ RSpec::Matchers.define_negated_matcher :have_values, :be_empty
- expect(evidences.first.data).not_to be_empty
- expect(evidences.first.data["summary"]).to match(/The Origin header was changed/)
- expect(evidences.size).to eq(3)
- expect(evidences.compact.size).to eq(2)
- expect(evidences.first).to be_a(::Gitlab::Ci::Reports::Security::Evidence)
+ it 'returns evidence object for each finding', :aggregate_failures do
+ all_evidences = report.findings.map(&:evidence)
+ evidences = all_evidences.compact
+ data = evidences.map(&:data)
+ summaries = evidences.map { |e| e.data["summary"] }
+
+ expect(all_evidences.size).to eq(7)
+ expect(evidences.size).to eq(2)
+ expect(evidences).to all( be_a(::Gitlab::Ci::Reports::Security::Evidence) )
+ expect(data).to all( have_values )
+ expect(summaries).to all( match(/The Origin header was changed/) )
end
end
diff --git a/spec/lib/gitlab/query_limiting/transaction_spec.rb b/spec/lib/gitlab/query_limiting/transaction_spec.rb
index 32a31f091bb..27da1f23556 100644
--- a/spec/lib/gitlab/query_limiting/transaction_spec.rb
+++ b/spec/lib/gitlab/query_limiting/transaction_spec.rb
@@ -85,6 +85,12 @@ RSpec.describe Gitlab::QueryLimiting::Transaction do
expect do
transaction.increment(described_class::GEO_NODES_LOAD)
transaction.increment(described_class::LICENSES_LOAD)
+ transaction.increment('SELECT a.attname, a.other_column FROM pg_attribute a')
+ transaction.increment('SELECT x.foo, a.attname FROM some_table x JOIN pg_attribute a')
+ transaction.increment(<<-SQL)
+ SELECT a.attname, a.other_column
+ FROM pg_attribute a
+ SQL
end.not_to change(transaction, :count)
end
end
diff --git a/spec/models/deploy_token_spec.rb b/spec/models/deploy_token_spec.rb
index 86eb38814ed..635326eeadc 100644
--- a/spec/models/deploy_token_spec.rb
+++ b/spec/models/deploy_token_spec.rb
@@ -73,10 +73,10 @@ RSpec.describe DeployToken do
describe '#ensure_token' do
it 'ensures a token' do
- deploy_token.token = nil
+ deploy_token.token_encrypted = nil
deploy_token.save!
- expect(deploy_token.token).not_to be_empty
+ expect(deploy_token.token_encrypted).not_to be_empty
end
end
diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb
index 46361e49d5e..6fa455cbfca 100644
--- a/spec/requests/api/graphql/ci/runner_spec.rb
+++ b/spec/requests/api/graphql/ci/runner_spec.rb
@@ -11,7 +11,8 @@ RSpec.describe 'Query.runner(id)' do
let_it_be(:active_instance_runner) do
create(:ci_runner, :instance, description: 'Runner 1', contacted_at: 2.hours.ago,
active: true, version: 'adfe156', revision: 'a', locked: true, ip_address: '127.0.0.1', maximum_timeout: 600,
- access_level: 0, tag_list: %w[tag1 tag2], run_untagged: true, executor_type: :custom)
+ access_level: 0, tag_list: %w[tag1 tag2], run_untagged: true, executor_type: :custom,
+ maintenance_note: 'Test maintenance note')
end
let_it_be(:inactive_instance_runner) do
@@ -64,6 +65,7 @@ RSpec.describe 'Query.runner(id)' do
'executorName' => runner.executor_type&.dasherize,
'architectureName' => runner.architecture,
'platformName' => runner.platform,
+ 'maintenanceNote' => runner.maintenance_note,
'jobCount' => 0,
'jobs' => a_hash_including("count" => 0, "nodes" => [], "pageInfo" => anything),
'projectCount' => nil,
diff --git a/spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb b/spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb
index 98963f57341..90956e7b4ea 100644
--- a/spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb
+++ b/spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb
@@ -39,8 +39,6 @@ RSpec.describe Clusters::Kubernetes::CreateOrUpdateNamespaceService, '#execute'
stub_kubeclient_put_role_binding(api_url, Clusters::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME, namespace: namespace)
stub_kubeclient_put_role(api_url, Clusters::Kubernetes::GITLAB_CROSSPLANE_DATABASE_ROLE_NAME, namespace: namespace)
stub_kubeclient_put_role_binding(api_url, Clusters::Kubernetes::GITLAB_CROSSPLANE_DATABASE_ROLE_BINDING_NAME, namespace: namespace)
- stub_kubeclient_put_role(api_url, Clusters::Kubernetes::GITLAB_CILIUM_ROLE_NAME, namespace: namespace)
- stub_kubeclient_put_role_binding(api_url, Clusters::Kubernetes::GITLAB_CILIUM_ROLE_BINDING_NAME, namespace: namespace)
stub_kubeclient_get_secret(
api_url,
diff --git a/spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb b/spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb
index 11045dfe950..a4f018aec0c 100644
--- a/spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb
+++ b/spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb
@@ -147,8 +147,6 @@ RSpec.describe Clusters::Kubernetes::CreateOrUpdateServiceAccountService do
stub_kubeclient_put_role_binding(api_url, Clusters::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME, namespace: namespace)
stub_kubeclient_put_role(api_url, Clusters::Kubernetes::GITLAB_CROSSPLANE_DATABASE_ROLE_NAME, namespace: namespace)
stub_kubeclient_put_role_binding(api_url, Clusters::Kubernetes::GITLAB_CROSSPLANE_DATABASE_ROLE_BINDING_NAME, namespace: namespace)
- stub_kubeclient_put_role(api_url, Clusters::Kubernetes::GITLAB_CILIUM_ROLE_NAME, namespace: namespace)
- stub_kubeclient_put_role_binding(api_url, Clusters::Kubernetes::GITLAB_CILIUM_ROLE_BINDING_NAME, namespace: namespace)
end
it 'creates a namespace object' do
@@ -245,47 +243,6 @@ RSpec.describe Clusters::Kubernetes::CreateOrUpdateServiceAccountService do
)
)
end
-
- it 'creates a role granting cilium permissions to the service account' do
- subject
-
- expect(WebMock).to have_requested(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/roles/#{Clusters::Kubernetes::GITLAB_CILIUM_ROLE_NAME}").with(
- body: hash_including(
- metadata: {
- name: Clusters::Kubernetes::GITLAB_CILIUM_ROLE_NAME,
- namespace: namespace
- },
- rules: [{
- apiGroups: %w(cilium.io),
- resources: %w(ciliumnetworkpolicies),
- verbs: %w(get list create update patch)
- }]
- )
- )
- end
-
- it 'creates a role binding granting cilium permissions to the service account' do
- subject
-
- expect(WebMock).to have_requested(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings/#{Clusters::Kubernetes::GITLAB_CILIUM_ROLE_BINDING_NAME}").with(
- body: hash_including(
- metadata: {
- name: Clusters::Kubernetes::GITLAB_CILIUM_ROLE_BINDING_NAME,
- namespace: namespace
- },
- roleRef: {
- apiGroup: 'rbac.authorization.k8s.io',
- kind: 'Role',
- name: Clusters::Kubernetes::GITLAB_CILIUM_ROLE_NAME
- },
- subjects: [{
- kind: 'ServiceAccount',
- name: service_account_name,
- namespace: namespace
- }]
- )
- )
- end
end
end
end
diff --git a/spec/support/helpers/filtered_search_helpers.rb b/spec/support/helpers/filtered_search_helpers.rb
index 5b74f347a44..93122ca3d0c 100644
--- a/spec/support/helpers/filtered_search_helpers.rb
+++ b/spec/support/helpers/filtered_search_helpers.rb
@@ -274,6 +274,10 @@ module FilteredSearchHelpers
expect(page).to have_css '.gl-filtered-search-token', text: "Milestone != %#{value}"
end
+ def expect_epic_token(value)
+ expect(page).to have_css '.gl-filtered-search-token', text: "Epic = #{value}"
+ end
+
def expect_search_term(value)
value.split(' ').each do |term|
expect(page).to have_css '.gl-filtered-search-term', text: term