Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-05-17 18:09:20 +00:00
parent d88ab3545c
commit 6f05d35c31
81 changed files with 471 additions and 777 deletions

View file

@ -49,4 +49,3 @@ Are there any other stages or teams involved that need to be kept in the loop?
/label ~"feature flag" ~"type::feature" ~"feature::addition"
/assign DRI

View file

@ -475,7 +475,6 @@ Layout/ArgumentAlignment:
- 'ee/spec/services/ee/merge_requests/refresh_service_spec.rb'
- 'ee/spec/services/ee/protected_branches/create_service_spec.rb'
- 'ee/spec/services/epics/update_service_spec.rb'
- 'ee/spec/services/geo/file_download_service_spec.rb'
- 'ee/spec/services/geo/file_registry_removal_service_spec.rb'
- 'ee/spec/services/geo/repository_verification_primary_service_spec.rb'
- 'ee/spec/services/groups/recent_merge_requests_count_service_spec.rb'

View file

@ -2887,7 +2887,6 @@ Layout/LineLength:
- 'ee/spec/services/geo/blob_download_service_spec.rb'
- 'ee/spec/services/geo/blob_upload_service_spec.rb'
- 'ee/spec/services/geo/container_repository_sync_spec.rb'
- 'ee/spec/services/geo/file_download_service_spec.rb'
- 'ee/spec/services/geo/framework_repository_sync_service_spec.rb'
- 'ee/spec/services/geo/hashed_storage_attachments_event_store_spec.rb'
- 'ee/spec/services/geo/hashed_storage_migration_service_spec.rb'
@ -3131,7 +3130,6 @@ Layout/LineLength:
- 'ee/spec/workers/geo/batch/project_registry_scheduler_worker_spec.rb'
- 'ee/spec/workers/geo/container_repository_sync_dispatch_worker_spec.rb'
- 'ee/spec/workers/geo/destroy_worker_spec.rb'
- 'ee/spec/workers/geo/file_download_dispatch_worker_spec.rb'
- 'ee/spec/workers/geo/project_sync_worker_spec.rb'
- 'ee/spec/workers/geo/prune_event_log_worker_spec.rb'
- 'ee/spec/workers/geo/registry_sync_worker_spec.rb'

View file

@ -122,7 +122,6 @@ RSpec/AnyInstanceOf:
- ee/spec/workers/concerns/elastic/indexing_control_spec.rb
- ee/spec/workers/elastic_commit_indexer_worker_spec.rb
- ee/spec/workers/geo/design_repository_shard_sync_worker_spec.rb
- ee/spec/workers/geo/file_download_dispatch_worker_spec.rb
- ee/spec/workers/geo/registry_sync_worker_spec.rb
- ee/spec/workers/geo/repository_cleanup_worker_spec.rb
- ee/spec/workers/geo/repository_shard_sync_worker_spec.rb

View file

@ -178,7 +178,6 @@ RSpec/VerifiedDoubles:
- ee/spec/services/ee/service_ping/permit_data_categories_service_spec.rb
- ee/spec/services/ee/service_ping/service_ping_settings_spec.rb
- ee/spec/services/geo/blob_download_service_spec.rb
- ee/spec/services/geo/file_download_service_spec.rb
- ee/spec/services/geo/graphql_request_service_spec.rb
- ee/spec/services/geo/node_status_request_service_spec.rb
- ee/spec/services/geo/replication_toggle_request_service_spec.rb
@ -225,8 +224,6 @@ RSpec/VerifiedDoubles:
- ee/spec/workers/geo/design_repository_sync_worker_spec.rb
- ee/spec/workers/geo/destroy_worker_spec.rb
- ee/spec/workers/geo/event_worker_spec.rb
- ee/spec/workers/geo/file_download_dispatch_worker_spec.rb
- ee/spec/workers/geo/file_download_worker_spec.rb
- ee/spec/workers/geo/metrics_update_worker_spec.rb
- ee/spec/workers/geo/prune_event_log_worker_spec.rb
- ee/spec/workers/geo/registry_sync_worker_spec.rb

View file

@ -544,4 +544,4 @@ gem 'ipaddress', '~> 0.8.3'
gem 'parslet', '~> 1.8'
gem 'ipynbdiff', '0.4.6'
gem 'ipynbdiff', '0.4.7'

View file

@ -665,7 +665,7 @@ GEM
invisible_captcha (1.1.0)
rails (>= 4.2)
ipaddress (0.8.3)
ipynbdiff (0.4.6)
ipynbdiff (0.4.7)
diffy (~> 3.3)
json (~> 2.5, >= 2.5.1)
jaeger-client (1.1.0)
@ -1543,7 +1543,7 @@ DEPENDENCIES
icalendar
invisible_captcha (~> 1.1.0)
ipaddress (~> 0.8.3)
ipynbdiff (= 0.4.6)
ipynbdiff (= 0.4.7)
jira-ruby (~> 2.1.4)
js_regex (~> 3.7)
json (~> 2.5.1)

View file

@ -1,10 +1,14 @@
import $ from 'jquery';
import { loadingIconForLegacyJS } from '~/loading_icon_for_legacy_js';
export default class FileTemplateSelector {
constructor(mediator) {
this.mediator = mediator;
this.$dropdown = null;
this.$wrapper = null;
this.dropdown = null;
this.wrapper = null;
}
init() {
@ -12,18 +16,21 @@ export default class FileTemplateSelector {
this.$dropdown = $(cfg.dropdown);
this.$wrapper = $(cfg.wrapper);
this.$dropdownIcon = this.$wrapper.find('.dropdown-menu-toggle-icon');
this.$loadingIcon = $(
'<div class="gl-spinner gl-spinner-orange gl-spinner-sm gl-absolute gl-top-3 gl-right-3 gl-display-none"></div>',
).insertAfter(this.$dropdownIcon);
this.$dropdownToggleText = this.$wrapper.find('.dropdown-toggle-text');
this.dropdown = document.querySelector(cfg.dropdown);
this.wrapper = document.querySelector(cfg.wrapper);
this.dropdownIcon = this.wrapper.querySelector('.dropdown-menu-toggle-icon');
this.loadingIcon = loadingIconForLegacyJS({ classes: ['gl-display-none'] });
this.dropdown.appendChild(this.loadingIcon);
this.dropdownToggleText = this.wrapper.querySelector('.dropdown-toggle-text');
this.initDropdown();
this.selectInitialTemplate();
}
selectInitialTemplate() {
const template = this.$dropdown.data('selected');
const template = this.dropdown.dataset.selected;
if (!template) {
return;
@ -33,11 +40,11 @@ export default class FileTemplateSelector {
}
show() {
if (this.$dropdown === null) {
if (this.dropdown === null) {
this.init();
}
this.$wrapper.removeClass('hidden');
this.wrapper.classList.remove('hidden');
/**
* We set the focus on the dropdown that was just shown. This is done so that, after selecting
@ -49,36 +56,36 @@ export default class FileTemplateSelector {
* closed anymore.
*/
setTimeout(() => {
this.$dropdown.focus();
this.dropdown.focus();
}, 0);
}
hide() {
if (this.$dropdown !== null) {
this.$wrapper.addClass('hidden');
if (this.dropdown !== null) {
this.wrapper.classList.add('hidden');
}
}
isHidden() {
return !this.$wrapper || this.$wrapper.hasClass('hidden');
return !this.wrapper || this.wrapper.classList.contains('hidden');
}
getToggleText() {
return this.$dropdownToggleText.text();
return this.dropdownToggleText.textContent;
}
setToggleText(text) {
this.$dropdownToggleText.text(text);
this.dropdownToggleText.textContent = text;
}
renderLoading() {
this.$loadingIcon.removeClass('gl-display-none');
this.$dropdownIcon.addClass('gl-display-none');
this.loadingIcon.classList.remove('gl-display-none');
this.dropdownIcon.classList.add('gl-display-none');
}
renderLoaded() {
this.$loadingIcon.addClass('gl-display-none');
this.$dropdownIcon.removeClass('gl-display-none');
this.loadingIcon.classList.add('gl-display-none');
this.dropdownIcon.classList.remove('gl-display-none');
}
reportSelection(options) {

View file

@ -1,6 +1,7 @@
<script>
import { GlBadge, GlIcon } from '@gitlab/ui';
import Vue from 'vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { fetchPolicies } from '~/lib/graphql';
import { __ } from '~/locale';
import { IssuableType } from '~/issues/constants';
@ -43,6 +44,7 @@ export default {
GlBadge,
GlIcon,
},
mixins: [glFeatureFlagMixin()],
inject: {
query: { default: null },
projectPath: { default: null },
@ -69,7 +71,13 @@ export default {
},
computed: {
badgeClass() {
return CLASSES[this.state];
return [
CLASSES[this.state],
{
'gl-vertical-align-bottom':
this.issuableType === IssuableType.MergeRequest && this.glFeatures.updatedMrHeader,
},
];
},
badgeVariant() {
if (this.state === IssuableStates.Opened) {

View file

@ -30,7 +30,11 @@ export default {
GlLoadingIcon,
GlTooltip,
},
actionSizeClasses: ['gl-h-7 gl-w-7'],
styles: {
actionSizeClasses: ['gl-h-7 gl-w-7'],
flatLeftBorder: ['gl-rounded-bottom-left-none!', 'gl-rounded-top-left-none!'],
flatRightBorder: ['gl-rounded-bottom-right-none!', 'gl-rounded-top-right-none!'],
},
mixins: [glFeatureFlagMixin()],
props: {
columnTitle: {
@ -80,14 +84,18 @@ export default {
return {};
},
buttonBorderClass() {
return this.isUpstream ? 'gl-border-r-1!' : 'gl-border-l-1!';
buttonBorderClasses() {
return this.isUpstream
? ['gl-border-r-0!', ...this.$options.styles.flatRightBorder]
: ['gl-border-l-0!', ...this.$options.styles.flatLeftBorder];
},
buttonId() {
return `js-linked-pipeline-${this.pipeline.id}`;
},
cardSpacingClass() {
return this.isDownstream ? 'gl-pr-0' : '';
cardClasses() {
return this.isDownstream
? this.$options.styles.flatRightBorder
: this.$options.styles.flatLeftBorder;
},
expandedIcon() {
if (this.isUpstream) {
@ -213,7 +221,7 @@ export default {
<template>
<div
ref="linkedPipeline"
class="gl-h-full gl-display-flex! gl-border-solid gl-border-gray-100 gl-border-1"
class="gl-h-full gl-display-flex!"
:class="flexDirection"
data-qa-selector="linked_pipeline_container"
@mouseover="onDownstreamHovered"
@ -222,21 +230,21 @@ export default {
<gl-tooltip v-if="showCardTooltip" :target="() => $refs.linkedPipeline">
{{ cardTooltipText }}
</gl-tooltip>
<div class="gl-w-full gl-bg-white gl-p-3" :class="cardSpacingClass">
<div class="gl-display-flex gl-pr-3">
<ci-status
v-if="!pipelineIsLoading"
:status="pipelineStatus"
:size="24"
css-classes="gl-top-0 gl-pr-2"
/>
<div class="gl-bg-white gl-border gl-p-3 gl-rounded-lg gl-w-full" :class="cardClasses">
<div class="gl-display-flex gl-gap-x-3">
<ci-status v-if="!pipelineIsLoading" :status="pipelineStatus" :size="24" css-classes="" />
<div v-else class="gl-pr-3"><gl-loading-icon size="sm" inline /></div>
<div class="gl-display-flex gl-flex-direction-column gl-downstream-pipeline-job-width">
<div
class="gl-display-flex gl-downstream-pipeline-job-width gl-flex-direction-column gl-line-height-normal"
>
<span class="gl-text-truncate" data-testid="downstream-title">
{{ downstreamTitle }}
</span>
<div class="gl-text-truncate">
<gl-link class="gl-text-blue-500!" :href="pipeline.path" data-testid="pipelineLink"
<gl-link
class="gl-text-blue-500! gl-font-sm"
:href="pipeline.path"
data-testid="pipelineLink"
>#{{ pipeline.id }}</gl-link
>
</div>
@ -248,13 +256,13 @@ export default {
:loading="isActionLoading"
:icon="action.icon"
class="gl-rounded-full!"
:class="$options.actionSizeClasses"
:class="$options.styles.actionSizeClasses"
:aria-label="action.ariaLabel"
@click="action.method"
@mouseover="setActionTooltip(true)"
@mouseout="setActionTooltip(false)"
/>
<div v-else :class="$options.actionSizeClasses"></div>
<div v-else :class="$options.styles.actionSizeClasses"></div>
</div>
<div class="gl-pt-2">
<gl-badge size="sm" variant="info" data-testid="downstream-pipeline-label">
@ -265,8 +273,8 @@ export default {
<div class="gl-display-flex">
<gl-button
:id="buttonId"
class="gl-shadow-none! gl-rounded-0!"
:class="`js-pipeline-expand-${pipeline.id} ${buttonBorderClass}`"
class="gl-border! gl-shadow-none! gl-rounded-lg!"
:class="[`js-pipeline-expand-${pipeline.id}`, buttonBorderClasses]"
:icon="expandedIcon"
:aria-label="__('Expand pipeline')"
data-testid="expand-pipeline-button"

View file

@ -66,14 +66,13 @@ export default {
columnClass() {
const positionValues = {
right: 'gl-ml-6',
left: 'gl-mr-6',
left: 'gl-mx-6',
};
return `graph-position-${this.graphPosition} ${positionValues[this.graphPosition]}`;
},
computedTitleClasses() {
const positionalClasses = this.isUpstream
? ['gl-w-full', 'gl-text-right', 'gl-linked-pipeline-padding']
: [];
const positionalClasses = this.isUpstream ? ['gl-w-full', 'gl-linked-pipeline-padding'] : [];
return [...this.$options.titleClasses, ...positionalClasses];
},
@ -202,7 +201,7 @@ export default {
<li
v-for="pipeline in linkedPipelines"
:key="pipeline.id"
class="gl-display-flex gl-mb-4"
class="gl-display-flex gl-mb-3"
:class="{ 'gl-flex-direction-row-reverse': isUpstream }"
>
<linked-pipeline

View file

@ -1,7 +1,9 @@
<script>
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { mapGetters } from 'vuex';
import { __ } from '~/locale';
import { mapGetters, mapActions } from 'vuex';
import { __, sprintf } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import createFlash from '~/flash';
import eventHub from '~/sidebar/event_hub';
import editForm from './edit_form.vue';
@ -23,11 +25,11 @@ export default {
editForm,
GlIcon,
},
directives: {
GlTooltip: GlTooltipDirective,
},
mixins: [glFeatureFlagMixin()],
inject: ['fullPath'],
props: {
isEditable: {
required: true,
@ -41,6 +43,9 @@ export default {
},
computed: {
...mapGetters(['getNoteableData']),
isMergeRequest() {
return this.getNoteableData.targetType === 'merge_request' && this.glFeatures.movedMrSidebar;
},
issuableDisplayName() {
const isInIssuePage = this.getNoteableData.targetType === this.$options.issue;
return isInIssuePage ? __('issue') : __('merge request');
@ -66,17 +71,49 @@ export default {
},
methods: {
...mapActions(['updateLockedAttribute']),
toggleForm() {
if (this.isEditable) {
this.isLockDialogOpen = !this.isLockDialogOpen;
}
},
toggleLocked() {
this.isLoading = true;
this.updateLockedAttribute({
locked: !this.isLocked,
fullPath: this.fullPath,
})
.catch(() => {
const flashMessage = __(
'Something went wrong trying to change the locked state of this %{issuableDisplayName}',
);
createFlash({
message: sprintf(flashMessage, { issuableDisplayName: this.issuableDisplayName }),
});
})
.finally(() => {
this.isLoading = false;
});
},
},
};
</script>
<template>
<div class="block issuable-sidebar-item lock">
<li v-if="isMergeRequest" class="gl-new-dropdown-item">
<button type="button" class="dropdown-item" @click="toggleLocked">
<span class="gl-new-dropdown-item-text-wrapper">
<template v-if="isLocked">
{{ __('Unlock merge request') }}
</template>
<template v-else>
{{ __('Lock merge request') }}
</template>
</span>
</button>
</li>
<div v-else class="block issuable-sidebar-item lock">
<div
v-gl-tooltip.left.viewport="{ title: tooltipLabel }"
class="sidebar-collapsed-icon"

View file

@ -431,10 +431,7 @@ function mountLockComponent(store) {
return;
}
const { fullPath } = getSidebarOptions();
const dataNode = document.getElementById('js-lock-issue-data');
const initialData = JSON.parse(dataNode.innerHTML);
const { fullPath, editable } = getSidebarOptions();
// eslint-disable-next-line no-new
new Vue({
@ -447,7 +444,7 @@ function mountLockComponent(store) {
render: (createElement) =>
createElement(IssuableLockForm, {
props: {
isEditable: initialData.is_editable,
isEditable: editable,
},
}),
});

View file

@ -254,7 +254,7 @@ export default {
class="media-body gl-display-flex gl-flex-direction-row! gl-align-self-center"
data-testid="widget-extension-top-level"
>
<div class="gl-flex-grow-1">
<div class="gl-flex-grow-1" data-testid="widget-extension-top-level-summary">
<template v-if="isLoadingSummary">{{ widgetLoadingText }}</template>
<template v-else-if="hasFetchError">{{ widgetErrorText }}</template>
<div v-else>

View file

@ -139,7 +139,7 @@
}
.gl-downstream-pipeline-job-width {
width: 170px;
width: 8rem;
}
.gl-linked-pipeline-padding {

View file

@ -125,6 +125,7 @@
background-color: $white;
z-index: 200;
overflow: hidden;
}
.right-sidebar {
@ -196,7 +197,6 @@
.issuable-sidebar-header {
@include clearfix;
padding: $gl-padding 0;
border-bottom: 1px solid $border-gray-normal;
// This prevents the mess when resizing the sidebar
// of elements repositioning themselves..
width: $gutter-inner-width;

View file

@ -9,7 +9,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
after_action :verify_known_sign_in
protect_from_forgery except: [:saml, :cas3, :failure] + AuthHelper.saml_providers, with: :exception, prepend: true
protect_from_forgery except: [:cas3, :failure] + AuthHelper.saml_providers, with: :exception, prepend: true
feature_category :authentication_and_authorization

View file

@ -74,9 +74,9 @@ module Projects
end
def update
@metric = update_metrics_service(prometheus_metric).execute
@metric = prometheus_metric
if @metric.persisted?
if @metric.update(metrics_params)
redirect_to edit_project_integration_path(project, ::Integrations::Prometheus),
notice: _('Metric was successfully updated.')
else

View file

@ -91,7 +91,9 @@ module AuthHelper
end
def saml_providers
auth_providers.select { |provider| auth_strategy_class(provider) == 'OmniAuth::Strategies::SAML' }
auth_providers.select do |provider|
provider == :saml || auth_strategy_class(provider) == 'OmniAuth::Strategies::SAML'
end
end
def auth_strategy_class(provider)

View file

@ -27,8 +27,6 @@ module Integrations
after_commit :track_events
after_create_commit :create_default_alerts
scope :preload_project, -> { preload(:project) }
def show_active_box?
@ -168,12 +166,6 @@ module Integrations
manual_configuration_changed? && !manual_configuration?
end
def create_default_alerts
return unless project_id
::Prometheus::CreateDefaultAlertsWorker.perform_async(project_id)
end
def behind_iap?
manual_configuration? && google_iap_audience_client_id.present? && google_iap_service_account_json.present?
end

View file

@ -8,40 +8,12 @@ module Projects
def initialize(metric, params = {})
@metric = metric
@project = metric.project
@params = params.dup
end
protected
attr_reader :metric, :project, :params
def application
alert.environment.cluster_prometheus_adapter
end
def schedule_alert_update
return unless alert
return unless alert.environment
::Clusters::Applications::ScheduleUpdateService.new(
alert.environment.cluster_prometheus_adapter, project).execute
end
def alert
strong_memoize(:alert) { find_alert(metric) }
end
def find_alert(metric)
Projects::Prometheus::AlertsFinder
.new(project: project, metric: metric)
.execute
.first
end
def has_alert?
alert.present?
end
attr_reader :metric, :params
end
end
end

View file

@ -5,7 +5,6 @@ module Projects
module Metrics
class DestroyService < Metrics::BaseService
def execute
schedule_alert_update if has_alert?
metric.destroy
end
end

View file

@ -1,29 +0,0 @@
# frozen_string_literal: true
module Projects
module Prometheus
module Metrics
class UpdateService < Metrics::BaseService
def execute
metric.update!(params)
schedule_alert_update if requires_alert_update?
metric
end
private
def requires_alert_update?
has_alert? && (changing_title? || changing_query?)
end
def changing_title?
metric.previous_changes.include?(:title)
end
def changing_query?
metric.previous_changes.include?(:query)
end
end
end
end
end

View file

@ -1,105 +0,0 @@
# frozen_string_literal: true
# DEPRECATED: To be removed as part of https://gitlab.com/groups/gitlab-org/-/epics/5877
module Prometheus
class CreateDefaultAlertsService < BaseService
include Gitlab::Utils::StrongMemoize
attr_reader :project
DEFAULT_ALERTS = [
{
identifier: 'response_metrics_nginx_ingress_16_http_error_rate',
operator: 'gt',
threshold: 0.1
},
{
identifier: 'response_metrics_nginx_ingress_http_error_rate',
operator: 'gt',
threshold: 0.1
},
{
identifier: 'response_metrics_nginx_http_error_percentage',
operator: 'gt',
threshold: 0.1
}
].freeze
def initialize(project:)
@project = project
end
def execute
return ServiceResponse.error(message: 'Invalid project') unless project
return ServiceResponse.error(message: 'Invalid environment') unless environment
create_alerts
schedule_prometheus_update
ServiceResponse.success
end
private
def create_alerts
DEFAULT_ALERTS.each do |alert_hash|
identifier = alert_hash[:identifier]
next if alerts_by_identifier(environment).key?(identifier)
metric = metrics_by_identifier[identifier]
next unless metric
create_alert(alert: alert_hash, metric: metric)
end
end
def schedule_prometheus_update
return unless prometheus_adapter
::Clusters::Applications::ScheduleUpdateService.new(prometheus_adapter, project).execute
end
def prometheus_adapter
environment.cluster_prometheus_adapter
end
def metrics_by_identifier
strong_memoize(:metrics_by_identifier) do
metric_identifiers = DEFAULT_ALERTS.map { |alert| alert[:identifier] }
PrometheusMetricsFinder
.new(identifier: metric_identifiers, common: true)
.execute
.index_by(&:identifier)
end
end
def alerts_by_identifier(environment)
strong_memoize(:alerts_by_identifier) do
Projects::Prometheus::AlertsFinder
.new(project: project, metric: metrics_by_identifier.values, environment: environment)
.execute
.index_by { |alert| alert.prometheus_metric.identifier }
end
end
def environment
strong_memoize(:environment) do
Environments::EnvironmentsFinder.new(project, nil, name: 'production').execute.first ||
project.environments.first
end
end
def create_alert(alert:, metric:)
PrometheusAlert.create!(
project: project,
prometheus_metric: metric,
environment: environment,
threshold: alert[:threshold],
operator: alert[:operator]
)
rescue ActiveRecord::RecordNotUnique
# Ignore duplicate creations although it unlikely to happen
end
end
end

View file

@ -33,12 +33,14 @@
= _('Reopen')
= display_issuable_type
- 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
- unless issuable_author_is_current_user(@merge_request)
%li.gl-new-dropdown-item
= 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
- if moved_mr_sidebar_enabled?
%li.gl-new-dropdown-item#js-lock-entry-point

View file

@ -1,8 +1,7 @@
.detail-page-description.py-2
- if Feature.enabled?(:updated_mr_header, @project)
= render 'shared/issuable/status_box', issuable: @merge_request
.gl-display-inline.gl-vertical-align-top
= merge_request_header(@project, @merge_request)
= merge_request_header(@project, @merge_request)
- else
%h2.title.mb-0{ data: { qa_selector: 'title_content' } }
= markdown_field(@merge_request, :title)

View file

@ -8,18 +8,25 @@
- 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'
- is_merge_request = issuable_type === 'merge_request'
- moved_sidebar_enabled = moved_mr_sidebar_enabled? && is_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} #{'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' } }
.issuable-sidebar-header{ class: "#{'gl-pb-2! gl-md-display-flex gl-justify-content-end gl-md-display-none!' if moved_sidebar_enabled}" }
%a.gutter-toggle.float-right.js-sidebar-toggle.has-tooltip{ role: "button", class: "#{'gl-display-block' 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
- if signed_in && !moved_sidebar_enabled
.js-issuable-todo{ data: { project_path: issuable_sidebar[:project_full_path], iid: issuable_sidebar[:iid], id: issuable_sidebar[:id] } }
= form_for issuable_type, url: issuable_sidebar[:issuable_json_path], remote: true, html: { class: 'issuable-context-form inline-update js-issuable-update' } do |f|
.block.assignee.qa-assignee-block
- if signed_in && moved_sidebar_enabled
.block.to-do
.title.hide-collapsed.gl-font-weight-bold.gl-display-flex.gl-align-items-center.gl-justify-content-space-between.gl-mt-2{ class: 'gl-mb-0!' }
= _('To-Do')
.js-issuable-todo{ data: { project_path: issuable_sidebar[:project_full_path], iid: issuable_sidebar[:iid], id: issuable_sidebar[:id] } }
.block.assignee.qa-assignee-block{ class: "#{'gl-mt-3' if !signed_in && moved_sidebar_enabled}" }
= render "shared/issuable/sidebar_assignees", issuable_sidebar: issuable_sidebar, assignees: assignees, signed_in: signed_in
- if reviewers
@ -73,12 +80,10 @@
= render_if_exists 'shared/issuable/sidebar_cve_id_request', issuable_sidebar: issuable_sidebar
-# haml-lint:disable InlineJavaScript
%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 && !moved_sidebar_enabled
.js-sidebar-subscriptions-entry-point
- if !moved_sidebar_enabled
#js-lock-entry-point
- if signed_in
.js-sidebar-subscriptions-entry-point
.js-sidebar-participants-entry-point

View file

@ -2,7 +2,8 @@
- badge_icon = state_name_with_icon(issuable)[1]
- badge_variant = issuable.open? ? :success : issuable.merged? ? :info : :danger
- badge_status_class = issuable.open? ? 'issuable-status-badge-open' : issuable.merged? ? 'issuable-status-badge-merged' : 'issuable-status-badge-closed'
- badge_classes = "js-mr-status-box issuable-status-badge gl-mr-3 #{badge_status_class}"
- updated_mr_header_enabled = Feature.enabled?(:updated_mr_header, @project) && issuable.is_a?(MergeRequest)
- badge_classes = "js-mr-status-box issuable-status-badge gl-mr-3 #{badge_status_class} #{'gl-vertical-align-bottom' if updated_mr_header_enabled}"
= gl_badge_tag({ variant: badge_variant, icon: badge_icon, icon_classes: 'gl-mr-0!' }, { class: badge_classes, data: { project_path: issuable.project.path_with_namespace, iid: issuable.iid, issuable_type: 'merge_request', state: issuable.state } }) do
%span.gl-display-none.gl-sm-display-block.gl-ml-2

View file

@ -41,6 +41,8 @@ module Reenqueuer
end
def perform(*args)
set_custom_lease_key(*args) if self.respond_to?(:set_custom_lease_key)
try_obtain_lease do
reenqueue(*args) do
ensure_minimum_duration(minimum_duration) do

View file

@ -13,19 +13,7 @@ module Prometheus
idempotent!
def perform(project_id)
project = Project.find_by_id(project_id)
return unless project
result = ::Prometheus::CreateDefaultAlertsService.new(project: project).execute
log_info(result.message) if result.error?
end
private
def log_info(message)
logger.info(structured_payload(message: message))
# No-op Will be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/360756
end
end
end

View file

@ -582,11 +582,6 @@ production: &base
geo_secondary_registry_consistency_worker:
cron: "* * * * *"
# GitLab Geo file download dispatch worker
# NOTE: This will only take effect if Geo is enabled (secondary nodes only)
geo_file_download_dispatch_worker:
cron: "*/1 * * * *"
# GitLab Geo registry sync worker (for backfilling)
# NOTE: This will only take effect if Geo is enabled (secondary nodes only)
geo_registry_sync_worker:

View file

@ -664,9 +664,6 @@ Gitlab.ee do
Settings.cron_jobs['geo_secondary_usage_data_cron_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['geo_secondary_usage_data_cron_worker']['cron'] ||= '0 0 * * 0'
Settings.cron_jobs['geo_secondary_usage_data_cron_worker']['job_class'] ||= 'Geo::SecondaryUsageDataCronWorker'
Settings.cron_jobs['geo_file_download_dispatch_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['geo_file_download_dispatch_worker']['cron'] ||= '*/1 * * * *'
Settings.cron_jobs['geo_file_download_dispatch_worker']['job_class'] ||= 'Geo::FileDownloadDispatchWorker'
Settings.cron_jobs['geo_registry_sync_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['geo_registry_sync_worker']['cron'] ||= '*/1 * * * *'
Settings.cron_jobs['geo_registry_sync_worker']['job_class'] ||= 'Geo::RegistrySyncWorker'

View file

@ -4,6 +4,6 @@
reporter: joshlambert
breaking_change: true
body: |
PostgreSQL 12 will be the minimum required version in GitLab 14.0. It offers [significant improvements](https://www.postgresql.org/about/news/postgresql-12-released-1976/) to indexing, partitioning, and general performance benefits.
GitLab 14.0 requires PostgreSQL 12 or later. It offers [significant improvements](https://www.postgresql.org/about/news/postgresql-12-released-1976/) to indexing, partitioning, and general performance benefits.
Starting in GitLab 13.7, all new installations default to version 12. From GitLab 13.8, single-node instances are automatically upgraded as well. If you aren't ready to upgrade, you can [opt out of automatic upgrades](https://docs.gitlab.com/omnibus/settings/database.html#opt-out-of-automatic-postgresql-upgrades).
Starting in GitLab 13.7, all new installations default to PostgreSQL version 12. From GitLab 13.8, single-node instances are automatically upgraded as well. If you aren't ready to upgrade, you can [opt out of automatic upgrades](https://docs.gitlab.com/omnibus/settings/database.html#opt-out-of-automatic-postgresql-upgrades).

View file

@ -4,6 +4,6 @@ classes:
- Ci::BuildNeed
feature_categories:
- pipeline_authoring
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/e7ee84aad4237eaa16f2aba75b4d2c7860625c9d
description: Dependencies for a specific CI/CD job.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/31328
milestone: '12.2'

View file

@ -1,9 +0,0 @@
---
table_name: geo_job_artifact_deleted_events
classes:
- Geo::JobArtifactDeletedEvent
feature_categories:
- geo_replication
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3935
milestone: '10.4'

View file

@ -0,0 +1,29 @@
# frozen_string_literal: true
class RemoveJobArtifactDeprecatedGeoFields < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
def up
with_lock_retries do
remove_column :geo_event_log, :job_artifact_deleted_event_id, :bigint
end
end
def down
with_lock_retries do
unless column_exists?(:geo_event_log, :job_artifact_deleted_event_id)
add_column(:geo_event_log, :job_artifact_deleted_event_id, :bigint)
end
end
add_concurrent_foreign_key :geo_event_log, :geo_job_artifact_deleted_events,
column: :job_artifact_deleted_event_id,
name: 'fk_176d3fbb5d',
on_delete: :cascade
add_concurrent_index :geo_event_log,
:job_artifact_deleted_event_id,
name: 'index_geo_event_log_on_job_artifact_deleted_event_id',
where: "(job_artifact_deleted_event_id IS NOT NULL)"
end
end

View file

@ -0,0 +1,16 @@
# frozen_string_literal: true
class RemoveJobArtifactDeletedEventTable < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
def up
drop_table :geo_job_artifact_deleted_events
end
def down
create_table :geo_job_artifact_deleted_events, id: :bigserial do |t|
t.bigint :job_artifact_id, null: false, index: true
t.string :file_path, null: false
end
end
end

View file

@ -0,0 +1 @@
aaf55d3f4a82789695ce1107d045a6230e111d5058733f72ccf4a1e5563a2636

View file

@ -0,0 +1 @@
728ce5b9d0986fa55a43841b2a5961f9716b3e2933d1e77e3ac00e30244c6f8e

View file

@ -15181,7 +15181,6 @@ CREATE TABLE geo_event_log (
hashed_storage_migrated_event_id bigint,
lfs_object_deleted_event_id bigint,
hashed_storage_attachments_event_id bigint,
job_artifact_deleted_event_id bigint,
reset_checksum_event_id bigint,
cache_invalidation_event_id bigint,
container_repository_updated_event_id bigint,
@ -15253,21 +15252,6 @@ CREATE SEQUENCE geo_hashed_storage_migrated_events_id_seq
ALTER SEQUENCE geo_hashed_storage_migrated_events_id_seq OWNED BY geo_hashed_storage_migrated_events.id;
CREATE TABLE geo_job_artifact_deleted_events (
id bigint NOT NULL,
file_path character varying NOT NULL,
job_artifact_id bigint NOT NULL
);
CREATE SEQUENCE geo_job_artifact_deleted_events_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE geo_job_artifact_deleted_events_id_seq OWNED BY geo_job_artifact_deleted_events.id;
CREATE TABLE geo_lfs_object_deleted_events (
id bigint NOT NULL,
lfs_object_id integer NOT NULL,
@ -22742,8 +22726,6 @@ ALTER TABLE ONLY geo_hashed_storage_attachments_events ALTER COLUMN id SET DEFAU
ALTER TABLE ONLY geo_hashed_storage_migrated_events ALTER COLUMN id SET DEFAULT nextval('geo_hashed_storage_migrated_events_id_seq'::regclass);
ALTER TABLE ONLY geo_job_artifact_deleted_events ALTER COLUMN id SET DEFAULT nextval('geo_job_artifact_deleted_events_id_seq'::regclass);
ALTER TABLE ONLY geo_lfs_object_deleted_events ALTER COLUMN id SET DEFAULT nextval('geo_lfs_object_deleted_events_id_seq'::regclass);
ALTER TABLE ONLY geo_node_namespace_links ALTER COLUMN id SET DEFAULT nextval('geo_node_namespace_links_id_seq'::regclass);
@ -24583,9 +24565,6 @@ ALTER TABLE ONLY geo_hashed_storage_attachments_events
ALTER TABLE ONLY geo_hashed_storage_migrated_events
ADD CONSTRAINT geo_hashed_storage_migrated_events_pkey PRIMARY KEY (id);
ALTER TABLE ONLY geo_job_artifact_deleted_events
ADD CONSTRAINT geo_job_artifact_deleted_events_pkey PRIMARY KEY (id);
ALTER TABLE ONLY geo_lfs_object_deleted_events
ADD CONSTRAINT geo_lfs_object_deleted_events_pkey PRIMARY KEY (id);
@ -27792,8 +27771,6 @@ CREATE INDEX index_geo_event_log_on_hashed_storage_attachments_event_id ON geo_e
CREATE INDEX index_geo_event_log_on_hashed_storage_migrated_event_id ON geo_event_log USING btree (hashed_storage_migrated_event_id) WHERE (hashed_storage_migrated_event_id IS NOT NULL);
CREATE INDEX index_geo_event_log_on_job_artifact_deleted_event_id ON geo_event_log USING btree (job_artifact_deleted_event_id) WHERE (job_artifact_deleted_event_id IS NOT NULL);
CREATE INDEX index_geo_event_log_on_lfs_object_deleted_event_id ON geo_event_log USING btree (lfs_object_deleted_event_id) WHERE (lfs_object_deleted_event_id IS NOT NULL);
CREATE INDEX index_geo_event_log_on_repositories_changed_event_id ON geo_event_log USING btree (repositories_changed_event_id) WHERE (repositories_changed_event_id IS NOT NULL);
@ -27812,8 +27789,6 @@ CREATE INDEX index_geo_hashed_storage_attachments_events_on_project_id ON geo_ha
CREATE INDEX index_geo_hashed_storage_migrated_events_on_project_id ON geo_hashed_storage_migrated_events USING btree (project_id);
CREATE INDEX index_geo_job_artifact_deleted_events_on_job_artifact_id ON geo_job_artifact_deleted_events USING btree (job_artifact_id);
CREATE INDEX index_geo_lfs_object_deleted_events_on_lfs_object_id ON geo_lfs_object_deleted_events USING btree (lfs_object_id);
CREATE INDEX index_geo_node_namespace_links_on_geo_node_id ON geo_node_namespace_links USING btree (geo_node_id);
@ -31214,9 +31189,6 @@ ALTER TABLE ONLY protected_branch_push_access_levels
ALTER TABLE ONLY internal_ids
ADD CONSTRAINT fk_162941d509 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY geo_event_log
ADD CONSTRAINT fk_176d3fbb5d FOREIGN KEY (job_artifact_deleted_event_id) REFERENCES geo_job_artifact_deleted_events(id) ON DELETE CASCADE;
ALTER TABLE ONLY incident_management_timeline_events
ADD CONSTRAINT fk_17a5fafbd4 FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;

View file

@ -11,8 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to
[enable the feature flag](../administration/feature_flags.md) named `inactive_projects_deletion`.
On GitLab.com, this feature is not available.
This feature is not ready for production use.
On GitLab.com, this feature is not available. This feature is not ready for production use.
Administrators of large GitLab instances can find that over time, projects become inactive and are no longer used.
These projects take up unnecessary disk space. With inactive project deletion, you can identify these projects, warn
@ -24,37 +23,35 @@ deleted, the action generates an audit event that it was performed by the first
You can configure inactive projects deletion or turn it off using the
[Application settings API](../api/settings.md#change-application-settings).
## Inactive project deletion options
The following options are available:
- **Delete inactive projects**: Enable or disable inactive project deletion.
- **Delete inactive projects that exceed**: Minimum size (MB) of inactive projects to be considered for deletion.
- `delete_inactive_projects`: Enable or disable inactive project deletion.
- `inactive_projects_min_size_mb`: Minimum size (MB) of inactive projects to be considered for deletion.
Projects smaller in size than this threshold aren't considered inactive.
- **Delete project after**: Minimum duration (months) after which a project is scheduled for deletion if it continues
be inactive.
- **Send warning email**: Minimum duration (months) after which a deletion warning email is sent if a project continues
to be inactive. The warning email is sent to users with the Owner and Maintainer roles of the inactive project.
This duration should be greater than the **Delete project after** duration.
- `inactive_projects_delete_after_months`: Minimum duration (months) after which a project is scheduled for deletion if
it continues be inactive.
- `inactive_projects_send_warning_email_after_months`: Minimum duration (months) after which a deletion warning email is
sent if a project continues to be inactive. The warning email is sent to users with the Owner and Maintainer roles of
the inactive project. This duration should be less than the `inactive_projects_delete_after_months` duration.
For example:
- Delete inactive projects: enabled.
- Delete inactive projects that exceed: 50.
- Delete project after: 12.
- Send warning email: 6.
- `delete_inactive_projects` enabled.
- `inactive_projects_min_size_mb` set to `50`.
- `inactive_projects_delete_after_months` set to `12`.
- `inactive_projects_send_warning_email_after_months` set to `6`.
In this scenario, when a project's size is:
- Less than 50 MB, the project is not considered inactive.
- Greater than 50 MB and it is inactive for:
- More than 6 months, a deletion warning is email is sent to users with the Owner and Maintainer role on the project
with the scheduled date of deletion.
- More than 6 months, a deletion warning is email is sent to users with the Owner and Maintainer role on the project
with the scheduled date of deletion.
- More than 12 months, the project is scheduled for deletion.
## Determine when a project was last active
There are several ways to view the project's activities and determine when project was last active:
You can view a project's activities and determine when the project was last active in the following ways:
1. Go to the [activity page](../user/project/working_with_projects.md#view-project-activity) for the project and view
the date of the latest event.

View file

@ -1239,12 +1239,6 @@ This content has been converted to a Rake task, see [verify database values can
Geo::JobArtifactRegistry.failed
```
#### Download artifact
```ruby
Gitlab::Geo::JobArtifactDownloader.new(:job_artifact, <artifact_id>).execute
```
#### Get a count of the synced artifacts
```ruby

View file

@ -19555,8 +19555,6 @@ Vulnerability sort values.
| <a id="vulnerabilitysortseverity_desc"></a>`severity_desc` | Severity in descending order. |
| <a id="vulnerabilitysortstate_asc"></a>`state_asc` | State in ascending order. |
| <a id="vulnerabilitysortstate_desc"></a>`state_desc` | State in descending order. |
| <a id="vulnerabilitysorttitle_asc"></a>`title_asc` **{warning-solid}** | **Deprecated** in 14.2. Deprecated due to performance issues. |
| <a id="vulnerabilitysorttitle_desc"></a>`title_desc` **{warning-solid}** | **Deprecated** in 14.2. Deprecated due to performance issues. |
### `VulnerabilityState`

View file

@ -92,7 +92,6 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `api_key` | string | true | User API token. User must have access to task. All comments are attributed to this user. |
| `restrict_to_branch` | string | false | Comma-separated list of branches to be are automatically inspected. Leave blank to include all branches. |
| `push_events` | boolean | false | Enable notifications for push events |
### Disable Asana integration
@ -128,7 +127,6 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `token` | string | true | The authentication token
| `subdomain` | string | false | The subdomain setting |
| `push_events` | boolean | false | Enable notifications for push events |
### Disable Assembla integration
@ -169,7 +167,6 @@ Parameters:
| `build_key` | string | true | Bamboo build plan key like KEY |
| `username` | string | true | A user with API access, if applicable |
| `password` | string | true | Password of the user |
| `push_events` | boolean | false | Enable notifications for push events |
### Disable Atlassian Bamboo CI integration
@ -206,7 +203,6 @@ Parameters:
| `new_issue_url` | string | true | New Issue URL |
| `issues_url` | string | true | Issue URL |
| `project_url` | string | true | Project URL |
| `push_events` | boolean | false | Enable notifications for push events |
### Disable Bugzilla integration
@ -244,6 +240,8 @@ Parameters:
| `project_url` | string | true | Pipeline URL. For example, `https://buildkite.com/example/pipeline` |
| `enable_ssl_verification` | boolean | false | DEPRECATED: This parameter has no effect since SSL verification is always enabled |
| `push_events` | boolean | false | Enable notifications for push events |
| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
| `tag_push_events` | boolean | false | Enable notifications for tag push events |
### Disable Buildkite integration
@ -281,7 +279,6 @@ Parameters:
| `token` | string | true | Campfire API token. To find it, log into Campfire and select **My info**. |
| `subdomain` | string | false | Campfire subdomain. Text between `https://` and `.campfirenow.com` when you're logged in. |
| `room` | string | false | Campfire room. The last part of the URL when you're in a room. |
| `push_events` | boolean | false | Enable notifications for push events. |
### Disable Campfire integration
@ -450,7 +447,6 @@ Parameters:
| `new_issue_url` | string | true | New Issue URL |
| `issues_url` | string | true | Issue URL |
| `project_url` | string | true | Project URL |
| `push_events` | boolean | false | Enable notifications for push events |
### Disable Custom Issue Tracker integration
@ -705,7 +701,6 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `token` | string | true | Flowdock Git source token |
| `push_events` | boolean | false | Enable notifications for push events |
### Disable Flowdock integration
@ -828,7 +823,6 @@ Parameters:
| `server_host` | string | false | localhost |
| `server_port` | integer | false | 6659 |
| `colorize_messages` | boolean | false | Colorize messages |
| `push_events` | boolean | false | Enable notifications for push events |
### Disable Irker (IRC gateway) integration
@ -1081,7 +1075,6 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `token` | string | true | The Pivotal Tracker token |
| `restrict_to_branch` | boolean | false | Comma-separated list of branches to automatically inspect. Leave blank to include all branches. |
| `push_events` | boolean | false | Enable notifications for push events |
### Disable Pivotal Tracker integration
@ -1156,7 +1149,6 @@ Parameters:
| `priority` | string | true | The priority |
| `device` | string | false | Leave blank for all active devices |
| `sound` | string | false | The sound of the notification |
| `push_events` | boolean | false | Enable notifications for push events |
### Disable Pushover integration
@ -1193,7 +1185,6 @@ Parameters:
| `new_issue_url` | string | true | New Issue URL |
| `project_url` | string | true | Project URL |
| `issues_url` | string | true | Issue URL |
| `push_events` | boolean | false | Enable notifications for push events |
### Disable Redmine integration
@ -1399,6 +1390,7 @@ Parameters:
| `username` | string | true | A user with permissions to trigger a manual build |
| `password` | string | true | The password of the user |
| `push_events` | boolean | false | Enable notifications for push events |
| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
### Disable JetBrains TeamCity CI integration
@ -1549,7 +1541,6 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `issues_url` | string | true | Issue URL |
| `project_url` | string | true | Project URL |
| `push_events` | boolean | false | Enable notifications for push events |
### Disable YouTrack integration

View file

@ -279,7 +279,7 @@ listed in the descriptions of the relevant settings.
| `default_snippet_visibility` | string | no | What visibility level new snippets receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. |
| `delayed_project_deletion` **(PREMIUM SELF)** | boolean | no | Enable delayed project deletion by default in new groups. Default is `false`. |
| `delayed_group_deletion` **(PREMIUM SELF)** | boolean | no | Enable delayed group deletion by default in new groups. Requires both `delayed_group_deletion` to be true and `deletion_adjourned_period` to be greater than 0. Default is `true`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352959) in GitLab 15.0. |
| `delete_inactive_projects` | boolean | no | Enable inactive project deletion feature. Default is `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84519) in GitLab 14.10. |
| `delete_inactive_projects` | boolean | no | Enable inactive project deletion feature. Default is `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84519) in GitLab 14.10. [Became operational](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689) in GitLab 15.0 (with feature flag `inactive_projects_deletion`, disabled by default). |
| `deletion_adjourned_period` **(PREMIUM SELF)** | integer | no | The number of days to wait before deleting a project or group that is marked for deletion. Value must be between 0 and 90. On every update, a hook on `deletion_adjourned_period` sets the value of `delayed_group_deletion` to `true` if `deletion_adjourned_period` is greater than 0 and `false` if `deletion_adjourned_period` is 0.
| `diff_max_patch_bytes` | integer | no | Maximum [diff patch size](../user/admin_area/diff_limits.md), in bytes. |
| `diff_max_files` | integer | no | Maximum [files in a diff](../user/admin_area/diff_limits.md). |
@ -356,9 +356,9 @@ listed in the descriptions of the relevant settings.
| `html_emails_enabled` | boolean | no | Enable HTML emails. |
| `import_sources` | array of strings | no | Sources to allow project import from, possible values: `github`, `bitbucket`, `bitbucket_server`, `gitlab`, `fogbugz`, `git`, `gitlab_project`, `gitea`, `manifest`, and `phabricator`. |
| `in_product_marketing_emails_enabled` | boolean | no | Enable [in-product marketing emails](../user/profile/notifications.md#global-notification-settings). Enabled by default. |
| `inactive_projects_delete_after_months` | integer | no | If `delete_inactive_projects` is `true`, the time (in months) to wait before deleting inactive projects. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84519) in GitLab 14.10. |
| `inactive_projects_min_size_mb` | integer | no | If `delete_inactive_projects` is `true`, the minimum repository size for projects to be checked for inactivity. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84519) in GitLab 14.10. |
| `inactive_projects_send_warning_email_after_months` | integer | no | If `delete_inactive_projects` is `true`, sets the time (in months) to wait before emailing maintainers that the project will be deleted because it is inactive. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84519) in GitLab 14.10. |
| `inactive_projects_delete_after_months` | integer | no | If `delete_inactive_projects` is `true`, the time (in months) to wait before deleting inactive projects. Default is `2`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84519) in GitLab 14.10. [Became operational](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689) in GitLab 15.0. |
| `inactive_projects_min_size_mb` | integer | no | If `delete_inactive_projects` is `true`, the minimum repository size for projects to be checked for inactivity. Default is `0`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84519) in GitLab 14.10. [Became operational](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689) in GitLab 15.0. |
| `inactive_projects_send_warning_email_after_months` | integer | no | If `delete_inactive_projects` is `true`, sets the time (in months) to wait before emailing maintainers that the project is scheduled be deleted because it is inactive. Default is `1`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84519) in GitLab 14.10. [Became operational](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689) in GitLab 15.0. |
| `invisible_captcha_enabled` | boolean | no | Enable Invisible CAPTCHA spam detection during sign-up. Disabled by default. |
| `issues_create_limit` | integer | no | Max number of issue creation requests per minute per user. Disabled by default.|
| `keep_latest_artifact` | boolean | no | Prevent the deletion of the artifacts from the most recent successful jobs, regardless of the expiry time. Enabled by default. |

View file

@ -27,6 +27,8 @@ If you are migrating from another CI/CD tool, view this documentation:
To use GitLab CI/CD:
1. [Ensure you have runners available](#ensure-you-have-runners-available) to run your jobs.
GitLab SaaS provides runners, so if you're using GitLab.com, you can skip this step.
If you don't have a runner, [install GitLab Runner](https://docs.gitlab.com/runner/install/)
and [register a runner](https://docs.gitlab.com/runner/register/) for your instance, project, or group.
1. [Create a `.gitlab-ci.yml` file](#create-a-gitlab-ciyml-file)

View file

@ -118,17 +118,6 @@ CI Job Artifacts and LFS objects are synced in a similar way as uploads,
but they are tracked by `Geo::JobArtifactRegistry`, and `Geo::LfsObjectRegistry`
models respectively.
#### File Download Dispatch worker
Also similar to the [Repository Sync worker](#repository-sync-worker),
there is a `Geo::FileDownloadDispatchWorker` class that is run
periodically to sync all uploads that aren't synced to the Geo
**secondary** site yet.
Files are copied via HTTP(s) and initiated via the
`/api/v4/geo/transfers/:type/:id` endpoint,
for example, `/api/v4/geo/transfers/lfs/123`.
## Authentication
To authenticate file transfers, each `GeoNode` record has two fields:

View file

@ -11,8 +11,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
GitLab Maintenance Mode **only** blocks writes from HTTP and SSH requests at the application level in a few key places within the rails application.
[Search the codebase for `maintenance_mode?`.](https://gitlab.com/search?search=maintenance_mode%3F&group_id=9970&project_id=278964&scope=blobs&search_code=false&snippets=false&repository_ref=)
- [the read-only database method](https://gitlab.com/gitlab-org/gitlab/-/blob/2425e9de50c678413ceaad6ee3bf66f42b7e228c/ee/lib/ee/gitlab/database.rb#L13), which toggles special behavior when we are not allowed to write to the database. [Search the codebase for `Gitlab::Database.read_only?`.](https://gitlab.com/search?search=Gitlab%3A%3ADatabase.read_only%3F&group_id=9970&project_id=278964&scope=blobs&search_code=false&snippets=false&repository_ref=)
- [the read-only middleware](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/ee/gitlab/middleware/read_only/controller.rb), where HTTP requests that cause database writes are blocked, unless explicitly allowed.
- [the read-only database method](https://gitlab.com/gitlab-org/gitlab/-/blob/2425e9de50c678413ceaad6ee3bf66f42b7e228c/ee/lib/ee/gitlab/database.rb#L13), which toggles special behavior when we are not allowed to write to the database. We use this method for possible places where writes could occur in GET requests. [Search the codebase for `Gitlab::Database.read_only?`.](https://gitlab.com/search?search=Gitlab%3A%3ADatabase.read_only%3F&group_id=9970&project_id=278964&scope=blobs&search_code=false&snippets=false&repository_ref=)
- [the read-only middleware](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/ee/gitlab/middleware/read_only/controller.rb), where HTTP requests that cause database writes are blocked, unless explicitly allowed (for example, GET requests).
- [Git push access via SSH is denied](https://gitlab.com/gitlab-org/gitlab/-/blob/2425e9de50c678413ceaad6ee3bf66f42b7e228c/ee/lib/ee/gitlab/git_access.rb#L13) by returning 401 when `gitlab-shell` POSTs to [`/internal/allowed`](internal_api/index.md) to [check if access is allowed](internal_api/index.md#git-authentication).
- [Container registry authentication service](https://gitlab.com/gitlab-org/gitlab/-/blob/2425e9de50c678413ceaad6ee3bf66f42b7e228c/ee/app/services/ee/auth/container_registry_authentication_service.rb#L12), where updates to the container registry are blocked.

View file

@ -206,7 +206,7 @@ By default, this `Gitlab::SQL::CTE` class forces materialization through adding
(this behavior is implemented using a custom Arel node `Gitlab::Database::AsWithMaterialized` under the surface).
WARNING:
We plan to drop the support for PostgreSQL 11. Upgrading to GitLab 14.0 requires PostgreSQL 12 or higher.
Upgrading to GitLab 14.0 requires PostgreSQL 12 or higher.
## Cached Queries

View file

@ -466,18 +466,22 @@ it('waits for an Ajax call', () => {
#### Vue rendering
To wait until a Vue component is re-rendered, use either of the equivalent
[`Vue.nextTick()`](https://vuejs.org/v2/api/#Vue-nextTick) or `vm.$nextTick()`.
Use [`nextTick()`](https://vuejs.org/v2/api/#Vue-nextTick) to wait until a Vue component is
re-rendered.
**in Jest:**
```javascript
it('renders something', () => {
import { nextTick } from 'vue';
// ...
it('renders something', async () => {
wrapper.setProps({ value: 'new value' });
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.text()).toBe('new value');
});
await nextTick();
expect(wrapper.text()).toBe('new value');
});
```

View file

@ -121,6 +121,8 @@ the following table) as these were used for development and testing:
|----------------|----------------------------|
| 13.0 | 11 |
| 14.0 | 12.10 |
| 15.0 | 12.0 |
| 16.0 (planned) | 13.0 |
You must also ensure the following extensions are loaded into every
GitLab database. [Read more about this requirement, and troubleshooting](postgresql_extensions.md).

View file

@ -197,6 +197,8 @@ GitLab 14.2 introduced an issue where a background migration named `BackfillDraf
GitLab 14.4 introduced an issue where a background migration named `PopulateTopicsTotalProjectsCountCache` can be permanently stuck in a **pending** state across upgrades when the instance lacks records that match the migration's target. To clean up this stuck migration, see the [14.4.0 version-specific instructions](#1440).
GitLab 14.5 introduced an issue where a background migration named `UpdateVulnerabilityOccurrencesLocation` can be permanently stuck in a **pending** state across upgrades when the instance lacks records that match the migration's target. To clean up this stuck migration, see the [14.5.0 version-specific instructions](#1450).
GitLab 14.8 introduced an issue where a background migration named `PopulateTopicsNonPrivateProjectsCount` can be permanently stuck in a **pending** state across upgrades. To clean up this stuck migration, see the [14.8.0 version-specific instructions](#1480).
GitLab 14.9 introduced an issue where a background migration named `ResetDuplicateCiRunnersTokenValuesOnProjects` can be permanently stuck in a **pending** state across upgrades when the instance lacks records that match the migration's target. To clean up this stuck migration, see the [14.9.0 version-specific instructions](#1490).
@ -545,6 +547,18 @@ or [init scripts](upgrading_from_source.md#configure-sysv-init-script) by [follo
For more information, refer to [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/331823).
- GitLab 14.5.0 includes a
[background migration `UpdateVulnerabilityOccurrencesLocation`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72788)
that may remain stuck permanently in a **pending** state when the instance lacks records that match the migration's target.
To clean up this stuck job, run the following in the [GitLab Rails Console](../administration/operations/rails_console.md):
```ruby
Gitlab::Database::BackgroundMigrationJob.pending.where(class_name: "UpdateVulnerabilityOccurrencesLocation").find_each do |job|
puts Gitlab::Database::BackgroundMigrationJob.mark_all_as_succeeded("UpdateVulnerabilityOccurrencesLocation", job.arguments)
end
```
### 14.4.4
- For [zero-downtime upgrades](zero_downtime.md) on a GitLab cluster with separate Web and API nodes, you need to enable the `paginated_tree_graphql_query` [feature flag](../administration/feature_flags.md#enable-or-disable-the-feature) _before_ upgrading GitLab Web nodes to 14.4.

View file

@ -1162,9 +1162,9 @@ as a [breaking change](https://docs.gitlab.com/ee/development/contributing/#brea
Before updating GitLab, review the details carefully to determine if you need to make any
changes to your code, settings, or workflow.
PostgreSQL 12 will be the minimum required version in GitLab 14.0. It offers [significant improvements](https://www.postgresql.org/about/news/postgresql-12-released-1976/) to indexing, partitioning, and general performance benefits.
GitLab 14.0 requires PostgreSQL 12 or later. It offers [significant improvements](https://www.postgresql.org/about/news/postgresql-12-released-1976/) to indexing, partitioning, and general performance benefits.
Starting in GitLab 13.7, all new installations default to version 12. From GitLab 13.8, single-node instances are automatically upgraded as well. If you aren't ready to upgrade, you can [opt out of automatic upgrades](https://docs.gitlab.com/omnibus/settings/database.html#opt-out-of-automatic-postgresql-upgrades).
Starting in GitLab 13.7, all new installations default to PostgreSQL version 12. From GitLab 13.8, single-node instances are automatically upgraded as well. If you aren't ready to upgrade, you can [opt out of automatic upgrades](https://docs.gitlab.com/omnibus/settings/database.html#opt-out-of-automatic-postgresql-upgrades).
### Redundant timestamp field from DORA metrics API payload

View file

@ -146,7 +146,7 @@ Remember to set `git -> bin_path` to `/usr/local/bin/git` in `config/gitlab.yml`
### 7. Update PostgreSQL
WARNING:
From GitLab 14.0, you must use at least PostgreSQL 12.
GitLab 14.0 requires at least PostgreSQL 12.
The latest version of GitLab might depend on a more recent PostgreSQL version
than what you are running. You may also have to enable some

View file

@ -260,6 +260,7 @@ Support depends on which scanner is used:
| Distribution | Grype | Trivy |
| -------------- | ----- | ----- |
| Alma Linux | | ✅ |
| Alpine Linux | ✅ | |
| Amazon Linux | ✅ | ✅ |
| BusyBox | ✅ | |
| CentOS | ✅ | ✅ |

View file

@ -61,7 +61,7 @@ so all projects within have access. For added security, you can also
To authorize the agent to access the GitLab project where you keep Kubernetes manifests:
1. On the top bar, select **Menu > Projects** and find the project that contains the agent configuration file (`config.yaml`).
1. Edit the file. Under the `ci_access` keyword, add the `projects` attribute.
1. Edit the `config.yaml` file. Under the `ci_access` keyword, add the `projects` attribute.
1. For the `id`, add the path:
```yaml
@ -84,7 +84,7 @@ Choose the context to run `kubectl` commands from your CI/CD scripts.
To authorize the agent to access all of the GitLab projects in a group or subgroup:
1. On the top bar, select **Menu > Projects** and find the project that contains the agent configuration file (`config.yaml`).
1. Edit the file. Under the `ci_access` keyword, add the `groups` attribute.
1. Edit the `config.yaml` file. Under the `ci_access` keyword, add the `groups` attribute.
1. For the `id`, add the path:
```yaml
@ -210,7 +210,7 @@ impersonation credentials in the following way:
| `agent.gitlab.com/username` | Contains the username of the user the CI job is running as. |
| `agent.gitlab.com/environment_slug` | Contains the slug of the environment. Only set if running in an environment. |
Example to restrict access by the CI/CD job's identity:
Example `config.yaml` to restrict access by the CI/CD job's identity:
```yaml
ci_access:

View file

@ -5,6 +5,10 @@ module Gitlab
module Saml
class Config
class << self
def enabled?
::AuthHelper.saml_providers.any?
end
def options
Gitlab::Auth::OAuth::Provider.config_for('saml')
end

View file

@ -32,3 +32,5 @@ module Gitlab
end
end
end
Gitlab::Auth::Saml::IdentityLinker.prepend_mod

View file

@ -217,7 +217,6 @@ geo_event_log: :gitlab_main
geo_events: :gitlab_main
geo_hashed_storage_attachments_events: :gitlab_main
geo_hashed_storage_migrated_events: :gitlab_main
geo_job_artifact_deleted_events: :gitlab_main
geo_lfs_object_deleted_events: :gitlab_main
geo_node_namespace_links: :gitlab_main
geo_nodes: :gitlab_main

View file

@ -959,7 +959,7 @@ module Gitlab
return if migration.finished?
Gitlab::Database::BackgroundMigration::BatchedMigrationRunner.finalize(job_class_name, table_name, column_name, job_arguments, connection: connection) if finalize
finalize_batched_background_migration(job_class_name: job_class_name, table_name: table_name, column_name: column_name, job_arguments: job_arguments) if finalize
unless migration.reload.finished? # rubocop:disable Cop/ActiveRecordAssociationReload
raise "Expected batched background migration for the given configuration to be marked as 'finished', " \

View file

@ -124,6 +124,14 @@ module Gitlab
end
def finalize_batched_background_migration(job_class_name:, table_name:, column_name:, job_arguments:)
database_name = Gitlab::Database.db_config_name(connection)
unless ActiveRecord::Base.configurations.primary?(database_name)
raise 'The `#finalize_background_migration` is currently not supported when running in decomposed database, ' \
'and this database is not `main:`. For more information visit: ' \
'https://docs.gitlab.com/ee/development/database/migrations_for_multiple_databases.html'
end
migration = Gitlab::Database::BackgroundMigration::BatchedMigration.find_for_configuration(job_class_name, table_name, column_name, job_arguments)
raise 'Could not find batched background migration' if migration.nil?

View file

@ -68,7 +68,9 @@ module Gitlab
Timeout.timeout(timeout_time) do
IpynbDiff.diff(source_diff.old_blob&.data, source_diff.new_blob&.data,
raise_if_invalid_nb: true, diffy_opts: { include_diff_info: true })&.tap do
raise_if_invalid_nb: true,
hide_images: true,
diffy_opts: { include_diff_info: true })&.tap do
log_event(LOG_IPYNBDIFF_GENERATED)
end
end

View file

@ -33,7 +33,6 @@ module Gitlab
def import
delete_stale_metrics
create_or_update_metrics
update_prometheus_environments
end
# rubocop: disable CodeReuse/ActiveRecord
@ -47,8 +46,6 @@ module Gitlab
affected_metric_ids << prometheus_metric.id
end
@affected_environment_ids += find_alerts(affected_metric_ids).get_environment_id
end
# rubocop: enable CodeReuse/ActiveRecord
@ -62,24 +59,9 @@ module Gitlab
return unless stale_metrics.exists?
delete_stale_alerts(stale_metrics)
stale_metrics.each_batch { |batch| batch.delete_all }
end
def delete_stale_alerts(stale_metrics)
stale_alerts = find_alerts(stale_metrics)
affected_environment_ids = stale_alerts.get_environment_id
return unless affected_environment_ids.present?
@affected_environment_ids += affected_environment_ids
stale_alerts.each_batch { |batch| batch.delete_all }
end
def find_alerts(metrics)
Projects::Prometheus::AlertsFinder.new(project: project, metric: metrics).execute
end
def prometheus_metrics_attributes
@prometheus_metrics_attributes ||= begin
Dashboard::Transformers::Yml::V1::PrometheusMetrics.new(
@ -89,19 +71,6 @@ module Gitlab
).execute
end
end
def update_prometheus_environments
affected_environments = ::Environment.for_id(@affected_environment_ids.flatten.uniq).for_project(project)
return unless affected_environments.exists?
affected_environments.each do |affected_environment|
::Clusters::Applications::ScheduleUpdateService.new(
affected_environment.cluster_prometheus_adapter,
project
).execute
end
end
end
end
end

View file

@ -787,6 +787,9 @@ msgstr ""
msgid "%{milliseconds}ms"
msgstr ""
msgid "%{minutesUsed} minutes"
msgstr ""
msgid "%{model_name} not found"
msgstr ""
@ -855,6 +858,9 @@ msgstr ""
msgid "%{openedIssues} open, %{closedIssues} closed"
msgstr ""
msgid "%{percentageUsed}%% used"
msgstr ""
msgid "%{percentage}%% issues closed"
msgstr ""
@ -21807,12 +21813,6 @@ msgstr ""
msgid "Job Failed #%{build_id}"
msgstr ""
msgid "Job artifact"
msgstr ""
msgid "Job artifacts"
msgstr ""
msgid "Job has been erased"
msgstr ""
@ -23030,6 +23030,9 @@ msgstr ""
msgid "Lock memberships to LDAP synchronization"
msgstr ""
msgid "Lock merge request"
msgstr ""
msgid "Lock not found"
msgstr ""
@ -39583,6 +39586,9 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
msgid "To-Do"
msgstr ""
msgid "To-Do List"
msgstr ""
@ -40369,6 +40375,9 @@ msgstr ""
msgid "Unlock account"
msgstr ""
msgid "Unlock merge request"
msgstr ""
msgid "Unlock more features with GitLab Ultimate"
msgstr ""
@ -40813,6 +40822,9 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
msgid "UsageQuota|Usage since %{usageSince}"
msgstr ""
msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit."
msgstr ""
@ -44405,6 +44417,9 @@ msgstr ""
msgid "ciReport|Found %{issuesWithCount}"
msgstr ""
msgid "ciReport|Full Report"
msgstr ""
msgid "ciReport|IaC Scanning"
msgstr ""
@ -44443,6 +44458,9 @@ msgstr ""
msgid "ciReport|Loading Code Quality report"
msgstr ""
msgid "ciReport|Manage Licenses"
msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""

View file

@ -47,7 +47,6 @@ RSpec.describe 'Database schema' do
events: %w[target_id],
forked_project_links: %w[forked_from_project_id],
geo_event_log: %w[hashed_storage_attachments_event_id],
geo_job_artifact_deleted_events: %w[job_artifact_id],
geo_lfs_object_deleted_events: %w[lfs_object_id],
geo_node_statuses: %w[last_event_id cursor_last_event_id],
geo_nodes: %w[oauth_application_id],

View file

@ -8,28 +8,91 @@ RSpec.describe 'Merge Request Discussion Lock', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project, author: user) }
let(:moved_mr_sidebar_enabled) { false }
before do
stub_feature_flags(moved_mr_sidebar: moved_mr_sidebar_enabled)
sign_in(user)
end
context 'when a user is a team member' do
before do
project.add_developer(user)
end
context 'moved sidebar flag disabled' do
context 'when a user is a team member' do
before do
project.add_developer(user)
end
context 'when the discussion is unlocked' do
it 'the user can lock the merge_request' do
visit project_merge_request_path(merge_request.project, merge_request)
context 'when the discussion is unlocked' do
it 'the user can lock the merge_request' do
visit project_merge_request_path(merge_request.project, merge_request)
expect(find('.issuable-sidebar')).to have_content('Unlocked')
expect(find('.issuable-sidebar')).to have_content('Unlocked')
page.within('.issuable-sidebar') do
find('.lock-edit').click
click_button('Lock')
page.within('.issuable-sidebar') do
find('.lock-edit').click
click_button('Lock')
end
expect(find('[data-testid="lock-status"]')).to have_content('Locked')
end
end
context 'when the discussion is locked' do
before do
merge_request.update_attribute(:discussion_locked, true)
visit project_merge_request_path(merge_request.project, merge_request)
end
expect(find('[data-testid="lock-status"]')).to have_content('Locked')
it 'the user can unlock the merge_request' do
expect(find('.issuable-sidebar')).to have_content('Locked')
page.within('.issuable-sidebar') do
find('.lock-edit').click
click_button('Unlock')
end
expect(find('[data-testid="lock-status"]')).to have_content('Unlocked')
end
end
end
context 'when a user is not a team member' do
context 'when the discussion is unlocked' do
before do
visit project_merge_request_path(merge_request.project, merge_request)
end
it 'the user can not lock the merge_request' do
expect(find('.issuable-sidebar')).to have_content('Unlocked')
expect(find('.issuable-sidebar')).not_to have_selector('.lock-edit')
end
end
context 'when the discussion is locked' do
before do
merge_request.update_attribute(:discussion_locked, true)
visit project_merge_request_path(merge_request.project, merge_request)
end
it 'the user can not unlock the merge_request' do
expect(find('.issuable-sidebar')).to have_content('Locked')
expect(find('.issuable-sidebar')).not_to have_selector('.lock-edit')
end
end
end
end
context 'moved sidebar flag enabled' do
let(:moved_mr_sidebar_enabled) { true }
context 'when the discussion is unlocked' do
before do
visit project_merge_request_path(merge_request.project, merge_request)
end
it 'the user can lock the merge_request' do
click_button 'Toggle dropdown'
expect(page).to have_content('Lock merge request')
end
end
@ -40,39 +103,9 @@ RSpec.describe 'Merge Request Discussion Lock', :js do
end
it 'the user can unlock the merge_request' do
expect(find('.issuable-sidebar')).to have_content('Locked')
click_button 'Toggle dropdown'
page.within('.issuable-sidebar') do
find('.lock-edit').click
click_button('Unlock')
end
expect(find('[data-testid="lock-status"]')).to have_content('Unlocked')
end
end
end
context 'when a user is not a team member' do
context 'when the discussion is unlocked' do
before do
visit project_merge_request_path(merge_request.project, merge_request)
end
it 'the user can not lock the merge_request' do
expect(find('.issuable-sidebar')).to have_content('Unlocked')
expect(find('.issuable-sidebar')).not_to have_selector('.lock-edit')
end
end
context 'when the discussion is locked' do
before do
merge_request.update_attribute(:discussion_locked, true)
visit project_merge_request_path(merge_request.project, merge_request)
end
it 'the user can not unlock the merge_request' do
expect(find('.issuable-sidebar')).to have_content('Locked')
expect(find('.issuable-sidebar')).not_to have_selector('.lock-edit')
expect(page).to have_content('Unlock merge request')
end
end
end

View file

@ -1,10 +1,11 @@
import $ from 'jquery';
import FileTemplateSelector from '~/blob/file_template_selector';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
describe('FileTemplateSelector', () => {
let subject;
let dropdown;
let wrapper;
const dropdown = '.dropdown';
const wrapper = '.wrapper';
const createSubject = () => {
subject = new FileTemplateSelector({});
@ -17,13 +18,16 @@ describe('FileTemplateSelector', () => {
afterEach(() => {
subject = null;
resetHTMLFixture();
});
describe('show method', () => {
beforeEach(() => {
dropdown = document.createElement('div');
wrapper = document.createElement('div');
wrapper.classList.add('hidden');
setHTMLFixture(`
<div class="wrapper hidden">
<div class="dropdown"></div>
</div>
`);
createSubject();
});
@ -37,25 +41,24 @@ describe('FileTemplateSelector', () => {
it('does not call init on subsequent calls', () => {
jest.spyOn(subject, 'init');
subject.show();
subject.show();
expect(subject.init).toHaveBeenCalledTimes(1);
});
it('removes hidden class from $wrapper', () => {
expect($(wrapper).hasClass('hidden')).toBe(true);
it('removes hidden class from wrapper', () => {
subject.init();
expect(subject.wrapper.classList.contains('hidden')).toBe(true);
subject.show();
expect($(wrapper).hasClass('hidden')).toBe(false);
expect(subject.wrapper.classList.contains('hidden')).toBe(false);
});
it('sets the focus on the dropdown', async () => {
subject.show();
jest.spyOn(subject.$dropdown, 'focus');
jest.spyOn(subject.dropdown, 'focus');
jest.runAllTimers();
expect(subject.$dropdown.focus).toHaveBeenCalled();
expect(subject.dropdown.focus).toHaveBeenCalled();
});
});
});

View file

@ -365,17 +365,17 @@ describe('Linked pipeline', () => {
describe('expand button', () => {
it.each`
pipelineType | anglePosition | borderClass | expanded
${downstreamProps} | ${'angle-right'} | ${'gl-border-l-1!'} | ${false}
${downstreamProps} | ${'angle-left'} | ${'gl-border-l-1!'} | ${true}
${upstreamProps} | ${'angle-left'} | ${'gl-border-r-1!'} | ${false}
${upstreamProps} | ${'angle-right'} | ${'gl-border-r-1!'} | ${true}
pipelineType | anglePosition | buttonBorderClasses | expanded
${downstreamProps} | ${'angle-right'} | ${'gl-border-l-0!'} | ${false}
${downstreamProps} | ${'angle-left'} | ${'gl-border-l-0!'} | ${true}
${upstreamProps} | ${'angle-left'} | ${'gl-border-r-0!'} | ${false}
${upstreamProps} | ${'angle-right'} | ${'gl-border-r-0!'} | ${true}
`(
'$pipelineType.columnTitle pipeline button icon should be $anglePosition with $borderClass if expanded state is $expanded',
({ pipelineType, anglePosition, borderClass, expanded }) => {
'$pipelineType.columnTitle pipeline button icon should be $anglePosition with $buttonBorderClasses if expanded state is $expanded',
({ pipelineType, anglePosition, buttonBorderClasses, expanded }) => {
createWrapper({ propsData: { ...pipelineType, expanded } });
expect(findExpandButton().props('icon')).toBe(anglePosition);
expect(findExpandButton().classes()).toContain(borderClass);
expect(findExpandButton().classes()).toContain(buttonBorderClasses);
},
);
});

View file

@ -37,6 +37,9 @@ describe('IssuableLockForm', () => {
const createComponent = ({ props = {} }) => {
wrapper = shallowMount(IssuableLockForm, {
store,
provide: {
fullPath: '',
},
propsData: {
isEditable: true,
...props,

View file

@ -500,6 +500,16 @@ RSpec.describe AuthHelper do
)
end
context 'when SAML is enabled without specifying a strategy class' do
before do
allow(Gitlab::Auth::OAuth::Provider).to receive(:providers).and_return([:saml])
end
it 'returns the saml provider' do
expect(saml_providers).to match_array([:saml])
end
end
context 'when configuration specifies no provider' do
before do
allow(Devise).to receive(:omniauth_providers).and_return([])

View file

@ -0,0 +1,19 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Auth::Saml::Config do
describe '.enabled?' do
subject { described_class.enabled? }
it { is_expected.to eq(false) }
context 'when SAML is enabled' do
before do
allow(Gitlab::Auth::OAuth::Provider).to receive(:providers).and_return([:saml])
end
it { is_expected.to eq(true) }
end
end
end

View file

@ -182,5 +182,26 @@ RSpec.describe Gitlab::Database::Migrations::BatchedBackgroundMigrationHelpers d
end.to raise_error(RuntimeError, 'Could not find batched background migration')
end
end
context 'when uses a CI connection', :reestablished_active_record_base do
before do
skip_if_multiple_databases_not_setup
ActiveRecord::Base.establish_connection(:ci) # rubocop:disable Database/EstablishConnection
end
it 'raises an exception' do
ci_migration = create(:batched_background_migration, :active)
expect do
migration.finalize_batched_background_migration(
job_class_name: ci_migration.job_class_name,
table_name: ci_migration.table_name,
column_name: ci_migration.column_name,
job_arguments: ci_migration.job_arguments
)
end.to raise_error /is currently not supported when running in decomposed/
end
end
end
end

View file

@ -141,5 +141,9 @@ RSpec.describe Gitlab::Diff::Rendered::Notebook::DiffFile do
expect(nb_file.highlighted_diff_lines[12].old_pos).to eq(18)
end
end
it 'computes de first line where the remove would appear' do
expect(nb_file.highlighted_diff_lines.map(&:text).join('')).to include('[Hidden Image Output]')
end
end
end

View file

@ -12,12 +12,6 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
subject { described_class.new(dashboard_hash, project: project, dashboard_path: dashboard_path) }
before do
allow_next_instance_of(::Clusters::Applications::ScheduleUpdateService) do |update_service|
allow(update_service).to receive(:execute)
end
end
context 'valid dashboard' do
let(:dashboard_hash) { load_sample_dashboard }
@ -45,13 +39,6 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
create(:prometheus_metric, existing_metric_attributes)
end
let!(:existing_alert) do
alert = create(:prometheus_alert, project: project, prometheus_metric: existing_metric)
existing_metric.prometheus_alerts << alert
alert
end
it 'updates existing PrometheusMetrics' do
subject.execute
@ -68,15 +55,6 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
expect { subject.execute }.to change { PrometheusMetric.count }.by(2)
end
it 'updates affected environments' do
expect(::Clusters::Applications::ScheduleUpdateService).to receive(:new).with(
existing_alert.environment.cluster_prometheus_adapter,
project
).and_return(double('ScheduleUpdateService', execute: true))
subject.execute
end
context 'with stale metrics' do
let!(:stale_metric) do
create(:prometheus_metric,
@ -87,13 +65,6 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
)
end
let!(:stale_alert) do
alert = create(:prometheus_alert, project: project, prometheus_metric: stale_metric)
stale_metric.prometheus_alerts << alert
alert
end
it 'updates existing PrometheusMetrics' do
subject.execute
@ -111,21 +82,6 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
expect { stale_metric.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
it 'deletes stale alert' do
subject.execute
expect { stale_alert.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
it 'updates affected environments' do
expect(::Clusters::Applications::ScheduleUpdateService).to receive(:new).with(
existing_alert.environment.cluster_prometheus_adapter,
project
).and_return(double('ScheduleUpdateService', execute: true))
subject.execute
end
end
end
end

View file

@ -122,34 +122,6 @@ RSpec.describe Integrations::Prometheus, :use_clean_rails_memory_store_caching,
end
end
describe 'callbacks' do
context 'after_create' do
let(:project) { create(:project) }
let(:integration) { build(:prometheus_integration, project: project) }
subject(:create_integration) { integration.save! }
it 'creates default alerts' do
expect(Prometheus::CreateDefaultAlertsWorker)
.to receive(:perform_async)
.with(project.id)
create_integration
end
context 'no project exists' do
let(:integration) { build(:prometheus_integration, :instance) }
it 'does not create default alerts' do
expect(Prometheus::CreateDefaultAlertsWorker)
.not_to receive(:perform_async)
create_integration
end
end
end
end
describe '#test' do
before do
integration.manual_configuration = true

View file

@ -12,17 +12,4 @@ RSpec.describe Projects::Prometheus::Metrics::DestroyService do
expect(PrometheusMetric.find_by(id: metric.id)).to be_nil
end
context 'when metric has a prometheus alert associated' do
it 'schedules a prometheus alert update' do
create(:prometheus_alert, project: metric.project, prometheus_metric: metric)
schedule_update_service = spy
allow(::Clusters::Applications::ScheduleUpdateService).to receive(:new).and_return(schedule_update_service)
subject.execute
expect(schedule_update_service).to have_received(:execute)
end
end
end

View file

@ -1,44 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::Prometheus::Metrics::UpdateService do
let(:metric) { create(:prometheus_metric) }
it 'updates the prometheus metric' do
expect do
described_class.new(metric, { title: "bar" }).execute
end.to change { metric.reload.title }.to("bar")
end
context 'when metric has a prometheus alert associated' do
let(:schedule_update_service) { spy }
before do
create(:prometheus_alert, project: metric.project, prometheus_metric: metric)
allow(::Clusters::Applications::ScheduleUpdateService).to receive(:new).and_return(schedule_update_service)
end
context 'when updating title' do
it 'schedules a prometheus alert update' do
described_class.new(metric, { title: "bar" }).execute
expect(schedule_update_service).to have_received(:execute)
end
end
context 'when updating query' do
it 'schedules a prometheus alert update' do
described_class.new(metric, { query: "sum(bar)" }).execute
expect(schedule_update_service).to have_received(:execute)
end
end
it 'does not schedule a prometheus alert update without title nor query being changed' do
described_class.new(metric, { y_label: "bar" }).execute
expect(schedule_update_service).not_to have_received(:execute)
end
end
end

View file

@ -1,92 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Prometheus::CreateDefaultAlertsService do
let_it_be(:project) { create(:project, :repository) }
let(:instance) { described_class.new(project: project) }
let(:expected_alerts) { described_class::DEFAULT_ALERTS }
describe '#execute' do
subject(:execute) { instance.execute }
shared_examples 'no alerts created' do
it 'does not create alerts' do
expect { execute }.not_to change { project.reload.prometheus_alerts.count }
end
end
context 'no environment' do
it_behaves_like 'no alerts created'
end
context 'environment exists' do
let_it_be(:environment) { create(:environment, project: project) }
context 'no found metric' do
it_behaves_like 'no alerts created'
end
context 'metric exists' do
before do
create_expected_metrics!
end
context 'alert exists already' do
before do
create_pre_existing_alerts!(environment)
end
it_behaves_like 'no alerts created'
end
it 'creates alerts' do
expect { execute }.to change { project.reload.prometheus_alerts.count }
.by(expected_alerts.size)
end
it 'does not schedule an update to prometheus' do
expect(::Clusters::Applications::ScheduleUpdateService).not_to receive(:new)
execute
end
context 'cluster with prometheus exists' do
let!(:cluster) { create(:cluster, :with_installed_prometheus, :provided_by_user, projects: [project]) }
it 'schedules an update to prometheus' do
expect_next_instance_of(::Clusters::Applications::ScheduleUpdateService) do |instance|
expect(instance).to receive(:execute)
end
execute
end
end
context 'multiple environments' do
let!(:production) { create(:environment, project: project, name: 'production') }
it 'uses the production environment' do
expect { execute }.to change { production.reload.prometheus_alerts.count }
.by(expected_alerts.size)
end
end
end
end
end
private
def create_expected_metrics!
expected_alerts.each do |alert_hash|
create(:prometheus_metric, :common, identifier: alert_hash.fetch(:identifier))
end
end
def create_pre_existing_alerts!(environment)
expected_alerts.each do |alert_hash|
metric = PrometheusMetric.for_identifier(alert_hash[:identifier]).first!
create(:prometheus_alert, prometheus_metric: metric, project: project, environment: environment)
end
end
end

View file

@ -19,7 +19,13 @@ RSpec.shared_examples 'reenqueuer' do
describe '#perform' do
it 'tries to obtain a lease' do
expect_to_obtain_exclusive_lease(subject.lease_key)
lease_key = if subject.respond_to?(:set_custom_lease_key)
subject.set_custom_lease_key(*job_args)
else
subject.lease_key
end
expect_to_obtain_exclusive_lease(lease_key)
subject_perform
end

View file

@ -240,7 +240,6 @@ RSpec.describe 'Every Sidekiq worker' do
'Geo::DesignRepositorySyncWorker' => 1,
'Geo::DestroyWorker' => 3,
'Geo::EventWorker' => 3,
'Geo::FileDownloadWorker' => 3,
'Geo::FileRegistryRemovalWorker' => 3,
'Geo::FileRemovalWorker' => 3,
'Geo::ProjectSyncWorker' => 1,

View file

@ -5,63 +5,9 @@ require 'spec_helper'
RSpec.describe Prometheus::CreateDefaultAlertsWorker do
let_it_be(:project) { create(:project) }
let(:worker) { described_class.new }
let(:logger) { worker.send(:logger) }
let(:service) { instance_double(Prometheus::CreateDefaultAlertsService) }
let(:service_result) { ServiceResponse.success }
subject { described_class.new.perform(project.id) }
before do
allow(Prometheus::CreateDefaultAlertsService)
.to receive(:new).with(project: project)
.and_return(service)
allow(service).to receive(:execute)
.and_return(service_result)
end
it_behaves_like 'an idempotent worker' do
let(:job_args) { [project.id] }
it 'calls the service' do
expect(service).to receive(:execute)
subject
end
context 'project is nil' do
let(:job_args) { [nil] }
it 'does not call the service' do
expect(service).not_to receive(:execute)
subject
end
end
context 'when service returns an error' do
let(:error_message) { 'some message' }
let(:service_result) { ServiceResponse.error(message: error_message) }
it 'succeeds and logs the error' do
expect(logger)
.to receive(:info)
.with(a_hash_including('message' => error_message))
.exactly(worker_exec_times).times
subject
end
end
end
context 'when service raises an exception' do
let(:error_message) { 'some exception' }
let(:exception) { StandardError.new(error_message) }
it 're-raises exception' do
allow(service).to receive(:execute).and_raise(exception)
expect { subject }.to raise_error(exception)
end
it 'does nothing' do
expect { subject }.not_to change { PrometheusAlert.count }
end
end