Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
d88ab3545c
commit
6f05d35c31
81 changed files with 471 additions and 777 deletions
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -544,4 +544,4 @@ gem 'ipaddress', '~> 0.8.3'
|
|||
|
||||
gem 'parslet', '~> 1.8'
|
||||
|
||||
gem 'ipynbdiff', '0.4.6'
|
||||
gem 'ipynbdiff', '0.4.7'
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -139,7 +139,7 @@
|
|||
}
|
||||
|
||||
.gl-downstream-pipeline-job-width {
|
||||
width: 170px;
|
||||
width: 8rem;
|
||||
}
|
||||
|
||||
.gl-linked-pipeline-padding {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -5,7 +5,6 @@ module Projects
|
|||
module Metrics
|
||||
class DestroyService < Metrics::BaseService
|
||||
def execute
|
||||
schedule_alert_update if has_alert?
|
||||
metric.destroy
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
|
@ -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
|
|
@ -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
|
1
db/schema_migrations/20220415124802
Normal file
1
db/schema_migrations/20220415124802
Normal file
|
@ -0,0 +1 @@
|
|||
aaf55d3f4a82789695ce1107d045a6230e111d5058733f72ccf4a1e5563a2636
|
1
db/schema_migrations/20220415124804
Normal file
1
db/schema_migrations/20220415124804
Normal file
|
@ -0,0 +1 @@
|
|||
728ce5b9d0986fa55a43841b2a5961f9716b3e2933d1e77e3ac00e30244c6f8e
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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. |
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
```
|
||||
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -260,6 +260,7 @@ Support depends on which scanner is used:
|
|||
| Distribution | Grype | Trivy |
|
||||
| -------------- | ----- | ----- |
|
||||
| Alma Linux | | ✅ |
|
||||
| Alpine Linux | ✅ | |
|
||||
| Amazon Linux | ✅ | ✅ |
|
||||
| BusyBox | ✅ | |
|
||||
| CentOS | ✅ | ✅ |
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -32,3 +32,5 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Gitlab::Auth::Saml::IdentityLinker.prepend_mod
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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', " \
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
@ -37,6 +37,9 @@ describe('IssuableLockForm', () => {
|
|||
const createComponent = ({ props = {} }) => {
|
||||
wrapper = shallowMount(IssuableLockForm, {
|
||||
store,
|
||||
provide: {
|
||||
fullPath: '',
|
||||
},
|
||||
propsData: {
|
||||
isEditable: true,
|
||||
...props,
|
||||
|
|
|
@ -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([])
|
||||
|
|
19
spec/lib/gitlab/auth/saml/config_spec.rb
Normal file
19
spec/lib/gitlab/auth/saml/config_spec.rb
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue