Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-05-16 21:09:09 +00:00
parent 7ff36fc6e9
commit cffe2c2c34
65 changed files with 1096 additions and 758 deletions

View File

@ -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
* @example
* // Respond to the alert being dismissed
* createAlert({ message: 'Message', onDismiss: () => {} });
*
* 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
*
* @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.
* 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 {'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,

View File

@ -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');

View File

@ -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;
}
}

View File

@ -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');

View File

@ -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"
>

View File

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

View File

@ -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 }}

View File

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

View File

@ -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"

View File

@ -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 />

View File

@ -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;

View File

@ -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%;
}
}

View File

@ -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;

View File

@ -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;
}

View File

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

View File

@ -66,5 +66,3 @@ module AlertManagement
end
end
end
AlertManagement::AlertsFinder.prepend_mod_with('AlertManagement::AlertsFinder')

View File

@ -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,

View File

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

View File

@ -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')

View File

@ -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]

View File

@ -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) }

View File

@ -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,

View File

@ -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) }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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}" }

View File

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

View File

@ -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')

View File

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

View File

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

View File

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

View File

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

View File

@ -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')

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 ""

View File

@ -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
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'
# 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
def refresh_subscription_seats
refresh_seats
::QA::Support::WaitForRequests.wait_for_requests
end
end
end
end

View File

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

View File

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

View File

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

View File

@ -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"'

View File

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

View File

@ -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 }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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"
}
}
},
{
"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"
}
]
},
{
"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": [
]
"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"
}
],
"remediations": [
{
"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"
},
"type": "dependency_scanning",
"start_time": "placeholder-value",
"end_time": "placeholder-value",
"status": "success"
}
},
"version": "14.0.2"
{
"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"
},
"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"
}
}
},
{
"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"
}]
},
{
"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"
},
"type": "dependency_scanning",
"start_time": "placeholder-value",
"end_time": "placeholder-value",
"status": "success"
},
"version": "14.0.2"
}

View File

@ -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();
});
});
});
});

View File

@ -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' } }

View File

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

View File

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

View File

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

View File

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

View File

@ -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,

View File

@ -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,

View File

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

View File

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