Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-09-20 18:13:03 +00:00
parent b962adf4c3
commit 29f6c0bff8
58 changed files with 314 additions and 341 deletions

View File

@ -3573,7 +3573,6 @@ Layout/LineLength:
- 'lib/gitlab/sidekiq_daemon/monitor.rb'
- 'lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb'
- 'lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/deduplicates_when_scheduling.rb'
- 'lib/gitlab/sidekiq_middleware/memory_killer.rb'
- 'lib/gitlab/sidekiq_middleware/server_metrics.rb'
- 'lib/gitlab/sidekiq_middleware/size_limiter/compressor.rb'
- 'lib/gitlab/sidekiq_versioning.rb'

View File

@ -592,7 +592,6 @@ Style/PercentLiteralDelimiters:
- 'lib/gitlab/search/abuse_detection.rb'
- 'lib/gitlab/search_context.rb'
- 'lib/gitlab/sidekiq_daemon/memory_killer.rb'
- 'lib/gitlab/sidekiq_middleware/memory_killer.rb'
- 'lib/gitlab/slash_commands/presenters/base.rb'
- 'lib/gitlab/ssh_public_key.rb'
- 'lib/gitlab/task_helpers.rb'

View File

@ -38,7 +38,6 @@ gem 'doorkeeper', '~> 5.5.0.rc2'
gem 'doorkeeper-openid_connect', '~> 1.7.5'
gem 'rexml', '~> 3.2.5'
gem 'ruby-saml', '~> 1.13.0'
gem 'omniauth-rails_csrf_protection'
gem 'omniauth', '~> 2.1.0'
gem 'omniauth-auth0', '~> 2.0.0'
gem 'omniauth-azure-activedirectory-v2', '~> 1.0'

View File

@ -958,9 +958,6 @@ GEM
omniauth (>= 1.9, < 3)
omniauth-oauth2-generic (0.2.2)
omniauth-oauth2 (~> 1.0)
omniauth-rails_csrf_protection (1.0.1)
actionpack (>= 4.2)
omniauth (~> 2.0)
omniauth-saml (2.0.0)
omniauth (~> 2.0)
ruby-saml (~> 1.9)
@ -1702,7 +1699,6 @@ DEPENDENCIES
omniauth-gitlab (~> 4.0.0)!
omniauth-google-oauth2 (~> 1.0.1)!
omniauth-oauth2-generic (~> 0.2.2)
omniauth-rails_csrf_protection
omniauth-salesforce (~> 1.0.5)!
omniauth-saml (~> 2.0.0)
omniauth-shibboleth (~> 1.3.0)

View File

@ -79,7 +79,7 @@ export default {
</script>
<template>
<div>
<div class="js-search-settings-section">
<token
v-for="(tokenData, tokenType) in enabledTokenTypes"
:key="tokenType"

View File

@ -196,7 +196,7 @@ export default {
<template>
<div>
<div v-if="loading" class="contributors-loader text-center">
<div v-if="loading" class="gl-text-center gl-pt-13">
<gl-loading-icon :inline="true" size="xl" />
</div>

View File

@ -86,7 +86,7 @@ export default {
},
methods: {
errorAlertDismissed() {
this.error = true;
this.error = false;
},
extractContacts(data) {
const contacts = data?.group?.contacts?.nodes || [];
@ -146,7 +146,7 @@ export default {
editButtonLabel: __('Edit'),
title: s__('Crm|Customer relations contacts'),
newContact: s__('Crm|New contact'),
errorText: __('Something went wrong. Please try again.'),
errorMsg: __('Something went wrong. Please try again.'),
},
EDIT_ROUTE_NAME,
NEW_ROUTE_NAME,
@ -176,7 +176,7 @@ export default {
<div>
<paginated-table-with-search-and-tabs
:show-items="true"
:show-error-msg="false"
:show-error-msg="error"
:i18n="$options.i18n"
:items="contacts.list"
:page-info="contacts.pageInfo"
@ -243,10 +243,7 @@ export default {
</template>
<template #empty>
<span v-if="error">
{{ $options.i18n.errorText }}
</span>
<span v-else>
<span>
{{ $options.i18n.emptyText }}
</span>
</template>

View File

@ -137,7 +137,7 @@ export default {
editButtonLabel: __('Edit'),
title: s__('Crm|Customer relations organizations'),
newOrganization: s__('Crm|New organization'),
errorText: __('Something went wrong. Please try again.'),
errorMsg: __('Something went wrong. Please try again.'),
},
EDIT_ROUTE_NAME,
NEW_ROUTE_NAME,
@ -167,7 +167,7 @@ export default {
<div>
<paginated-table-with-search-and-tabs
:show-items="true"
:show-error-msg="false"
:show-error-msg="error"
:i18n="$options.i18n"
:items="organizations.list"
:page-info="organizations.pageInfo"
@ -238,10 +238,7 @@ export default {
</template>
<template #empty>
<span v-if="error">
{{ $options.i18n.errorText }}
</span>
<span v-else>
<span>
{{ $options.i18n.emptyText }}
</span>
</template>

View File

@ -74,6 +74,9 @@ export default {
return utcDate.toISOString();
},
hasTimelineText() {
return this.timelineText.length > 0;
},
},
mounted() {
this.focusDate();
@ -167,6 +170,8 @@ export default {
variant="confirm"
category="primary"
class="gl-mr-3"
data-testid="save-button"
:disabled="!hasTimelineText"
:loading="isEventProcessed"
@click="handleSave(false)"
>
@ -177,6 +182,8 @@ export default {
variant="confirm"
category="secondary"
class="gl-mr-3 gl-ml-n2"
data-testid="save-and-add-button"
:disabled="!hasTimelineText"
:loading="isEventProcessed"
@click="handleSave(true)"
>

View File

@ -131,9 +131,9 @@ export default {
:message-url="view.message_url"
:config="$options.integrationViewConfigs[view.name]"
/>
</div>
<div class="col-sm-12">
<hr />
</div>
<div class="col-sm-12 js-hide-when-nothing-matches-search">
<gl-button
category="primary"
variant="confirm"

View File

@ -1,5 +1,5 @@
<script>
import { GlSearchBoxByType } from '@gitlab/ui';
import { GlEmptyState, GlSearchBoxByType } from '@gitlab/ui';
import { escapeRegExp } from 'lodash';
import {
EXCLUDED_NODES,
@ -96,6 +96,8 @@ const displayResults = ({ sectionSelector, expandSection, searchTerm }, matching
hideSectionsExcept(sectionSelector, sections);
sections.forEach(expandSection);
highlightText(matchingTextNodes, searchTerm);
return sections.length > 0;
};
const clearResults = (params) => {
@ -126,6 +128,7 @@ const search = (root, searchTerm) => {
export default {
components: {
GlEmptyState,
GlSearchBoxByType,
},
props: {
@ -137,6 +140,11 @@ export default {
type: String,
required: true,
},
hideWhenEmptySelector: {
type: String,
required: true,
default: null,
},
isExpandedFn: {
type: Function,
required: false,
@ -147,8 +155,16 @@ export default {
data() {
return {
searchTerm: '',
hasMatches: true,
};
},
watch: {
hasMatches(newHasMatches) {
document.querySelectorAll(this.hideWhenEmptySelector).forEach((section) => {
section.classList.toggle(HIDE_CLASS, !newHasMatches);
});
},
},
methods: {
search(value) {
this.searchTerm = value;
@ -161,11 +177,12 @@ export default {
};
clearResults(displayOptions);
this.hasMatches = true;
if (value.length) {
saveExpansionState(document.querySelectorAll(this.sectionSelector), displayOptions);
displayResults(displayOptions, search(this.searchRoot, this.searchTerm));
this.hasMatches = displayResults(displayOptions, search(this.searchRoot, this.searchTerm));
} else {
restoreExpansionState(displayOptions);
}
@ -181,10 +198,18 @@ export default {
};
</script>
<template>
<gl-search-box-by-type
:value="searchTerm"
:debounce="$options.TYPING_DELAY"
:placeholder="__('Search page')"
@input="search"
/>
<div>
<gl-search-box-by-type
:value="searchTerm"
:debounce="$options.TYPING_DELAY"
:placeholder="__('Search page')"
@input="search"
/>
<gl-empty-state
v-if="!hasMatches"
:title="__('No results found')"
:description="__('Edit your search and try again')"
/>
</div>
</template>

View File

@ -11,6 +11,7 @@ const mountSearch = ({ el }) =>
props: {
searchRoot: document.querySelector('#content-body'),
sectionSelector: '.js-search-settings-section, section.settings',
hideWhenEmptySelector: '.js-hide-when-nothing-matches-search',
isExpandedFn: isExpanded,
},
on: {

View File

@ -275,7 +275,7 @@ export default {
<template>
<div class="incident-management-list">
<gl-alert v-if="showErrorMsg" variant="danger" @dismiss="$emit('error-alert-dismissed')">
<p v-safe-html="serverErrorMessage || i18n.errorMsg"></p>
<span v-safe-html="serverErrorMessage || i18n.errorMsg"></span>
</gl-alert>
<div

View File

@ -27,12 +27,14 @@ module SendsBlob
private
def cached_blob?(blob, allow_caching: false)
stale = stale?(etag: blob.id) # The #stale? method sets cache headers.
stale =
if Feature.enabled?(:improve_blobs_cache_headers)
stale?(strong_etag: blob.id)
else
stale?(etag: blob.id)
end
# Because we are opinionated we set the cache headers ourselves.
response.cache_control[:public] = allow_caching
response.cache_control[:max_age] =
max_age =
if @ref && @commit && @ref == @commit.id # rubocop:disable Gitlab/ModuleWithInstanceVariables
# This is a link to a commit by its commit SHA. That means that the blob
# is immutable. The only reason to invalidate the cache is if the commit
@ -44,6 +46,16 @@ module SendsBlob
Blob::CACHE_TIME
end
# Because we are opinionated we set the cache headers ourselves.
if Feature.enabled?(:improve_blobs_cache_headers)
expires_in(max_age,
public: allow_caching, must_revalidate: true, stale_if_error: 5.minutes,
stale_while_revalidate: 1.minute, 's-maxage': 1.minute)
else
response.cache_control[:public] = allow_caching
response.cache_control[:max_age] = max_age
end
!stale
end

View File

@ -5,25 +5,26 @@
- @content_class = "limit-container-width" unless fluid_layout
- payload_class = 'js-service-ping-payload'
%h3= name
%section.js-search-settings-section
%h3= name
- if @service_ping_data_present
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-payload-preview-trigger gl-mr-2', data: { payload_selector: ".#{payload_class}" } } ) do
= gl_loading_icon(css_class: 'js-spinner gl-display-none', inline: true)
%span.js-text.gl-display-inline= _('Preview payload')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-payload-download-trigger gl-mr-2', data: { endpoint: usage_data_admin_application_settings_path(format: :json) } } ) do
= gl_loading_icon(css_class: 'js-spinner gl-display-none', inline: true)
%span.js-text.gl-display-inline= _('Download payload')
%pre.js-syntax-highlight.code.highlight.gl-mt-2.gl-display-none{ class: payload_class, data: { endpoint: usage_data_admin_application_settings_path(format: :html) } }
- else
= render Pajamas::AlertComponent.new(variant: :warning,
dismissible: false,
title: _('Service Ping payload not found in the application cache')) do |c|
- if @service_ping_data_present
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-payload-preview-trigger gl-mr-2', data: { payload_selector: ".#{payload_class}" } } ) do
= gl_loading_icon(css_class: 'js-spinner gl-display-none', inline: true)
%span.js-text.gl-display-inline= _('Preview payload')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-payload-download-trigger gl-mr-2', data: { endpoint: usage_data_admin_application_settings_path(format: :json) } } ) do
= gl_loading_icon(css_class: 'js-spinner gl-display-none', inline: true)
%span.js-text.gl-display-inline= _('Download payload')
%pre.js-syntax-highlight.code.highlight.gl-mt-2.gl-display-none{ class: payload_class, data: { endpoint: usage_data_admin_application_settings_path(format: :html) } }
- else
= render Pajamas::AlertComponent.new(variant: :warning,
dismissible: false,
title: _('Service Ping payload not found in the application cache')) do |c|
= c.body do
- enable_service_ping_link_url = help_page_path('user/admin_area/settings/usage_statistics', anchor: 'enable-or-disable-usage-statistics')
- enable_service_ping_link = '<a href="%{url}">'.html_safe % { url: enable_service_ping_link_url }
- generate_manually_link_url = help_page_path('administration/troubleshooting/gitlab_rails_cheat_sheet', anchor: 'generate-service-ping')
- generate_manually_link = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: generate_manually_link_url }
= c.body do
- enable_service_ping_link_url = help_page_path('user/admin_area/settings/usage_statistics', anchor: 'enable-or-disable-usage-statistics')
- enable_service_ping_link = '<a href="%{url}">'.html_safe % { url: enable_service_ping_link_url }
- generate_manually_link_url = help_page_path('administration/troubleshooting/gitlab_rails_cheat_sheet', anchor: 'generate-service-ping')
- generate_manually_link = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: generate_manually_link_url }
= html_escape(s_('%{enable_service_ping_link_start}Enable%{link_end} or %{generate_manually_link_start}generate%{link_end} Service Ping to preview and download service usage data payload.')) % { enable_service_ping_link_start: enable_service_ping_link, generate_manually_link_start: generate_manually_link, link_end: '</a>'.html_safe }
= html_escape(s_('%{enable_service_ping_link_start}Enable%{link_end} or %{generate_manually_link_start}generate%{link_end} Service Ping to preview and download service usage data payload.')) % { enable_service_ping_link_start: enable_service_ping_link, generate_manually_link_start: generate_manually_link, link_end: '</a>'.html_safe }

View File

@ -2,7 +2,7 @@
- page_title _("Projects")
- @content_class = "limit-container-width" unless fluid_layout
.card.gl-mt-3
.card.gl-mt-3.js-search-settings-section
.card-header
%strong= @group.name
projects:

View File

@ -4,7 +4,7 @@
- type_plural = _('group access tokens')
- @content_class = 'limit-container-width' unless fluid_layout
.row.gl-mt-3
.row.gl-mt-3.js-search-settings-section
.col-lg-4
%h4.gl-mt-0
= page_title

View File

@ -2,8 +2,9 @@
- page_title s_('Integrations|Group-level integration management')
- @content_class = 'limit-container-width' unless fluid_layout
%h3= s_('Integrations|Group-level integration management')
%section.js-search-settings-section
%h3= s_('Integrations|Group-level integration management')
- integrations_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: integrations_help_page_path }
%p= s_("Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}.").html_safe % { integrations_link_start: integrations_link_start, link_end: "</a>".html_safe }
= render 'shared/integrations/index', integrations: @integrations
- integrations_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: integrations_help_page_path }
%p= s_("Integrations|GitLab administrators can set up integrations that all projects in a group inherit and use by default. These integrations apply to all projects that don't already use custom settings. You can override custom settings for a project if the settings are necessary at that level. Learn more about %{integrations_link_start}group-level integration management%{link_end}.").html_safe % { integrations_link_start: integrations_link_start, link_end: "</a>".html_safe }
= render 'shared/integrations/index', integrations: @integrations

View File

@ -1,7 +1,7 @@
- page_title _('Active Sessions')
- @content_class = "limit-container-width" unless fluid_layout
.row.gl-mt-3
.row.gl-mt-3.js-search-settings-section
.col-lg-4.profile-settings-sidebar
%h4.gl-mt-0
= page_title

View File

@ -1,7 +1,7 @@
- page_title _('Authentication log')
- @content_class = "limit-container-width" unless fluid_layout
.row.gl-mt-3
.row.gl-mt-3.js-search-settings-section
.col-lg-4.profile-settings-sidebar
%h4.gl-mt-0
= page_title

View File

@ -1,7 +1,7 @@
- page_title _('Chat')
- @content_class = "limit-container-width" unless fluid_layout
.row.gl-mt-3
.row.gl-mt-3.js-search-settings-section
.col-lg-4.profile-settings-sidebar
%h4.gl-mt-0
= page_title

View File

@ -1,7 +1,7 @@
- page_title _('Emails')
- @content_class = "limit-container-width" unless fluid_layout
.row.gl-mt-3
.row.gl-mt-3.js-search-settings-section
.col-lg-4.profile-settings-sidebar
%h4.gl-mt-0
= page_title

View File

@ -1,7 +1,7 @@
- page_title _('GPG Keys')
- @content_class = "limit-container-width" unless fluid_layout
.row.gl-mt-3
.row.gl-mt-3.js-search-settings-section
.col-lg-4.profile-settings-sidebar
%h4.gl-mt-0
= page_title

View File

@ -1,7 +1,7 @@
- page_title _('SSH Keys')
- @content_class = "limit-container-width" unless fluid_layout
.row.gl-mt-3
.row.gl-mt-3.js-search-settings-section
.col-lg-4.profile-settings-sidebar
%h4.gl-mt-0
= page_title

View File

@ -10,7 +10,7 @@
%li= msg
= hidden_field_tag :notification_type, 'global'
.row.gl-mt-3
.row.gl-mt-3.js-search-settings-section
.col-lg-4.profile-settings-sidebar
%h4.gl-mt-0
= page_title

View File

@ -2,7 +2,7 @@
- page_title _('Password')
- @content_class = "limit-container-width" unless fluid_layout
.row.gl-mt-3
.row.gl-mt-3.js-search-settings-section
.col-lg-4.profile-settings-sidebar
%h4.gl-mt-0
= page_title

View File

@ -133,9 +133,11 @@
= f.gitlab_ui_checkbox_component :include_private_contributions,
s_('Profiles|Include private contributions on my profile'),
help_text: s_("Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information.")
%hr
= f.submit s_("Profiles|Update profile settings"), class: 'gl-button btn btn-confirm gl-mr-3 js-password-prompt-btn'
= link_to _("Cancel"), user_path(current_user), class: 'gl-button btn btn-default btn-cancel'
.row.js-hide-when-nothing-matches-search
.col-lg-12
%hr
= f.submit s_("Profiles|Update profile settings"), class: 'gl-button btn btn-confirm gl-mr-3 js-password-prompt-btn'
= link_to _("Cancel"), user_path(current_user), class: 'gl-button btn btn-default btn-cancel'
#password-prompt-modal

View File

@ -2,7 +2,7 @@
- breadcrumb_title _('Webhook Settings')
- page_title _('Webhooks')
.row.gl-mt-3
.row.gl-mt-3.js-search-settings-section
.col-lg-4
= render 'shared/web_hooks/title_and_docs', hook: @hook

View File

@ -1,7 +1,8 @@
- if Feature.enabled?(:use_pipeline_wizard_for_pages, @project.group)
#js-pages{ data: @pipeline_wizard_data }
%section.js-search-settings-section
- if Feature.enabled?(:use_pipeline_wizard_for_pages, @project.group)
#js-pages{ data: @pipeline_wizard_data }
- else
= render 'header'
- else
= render 'header'
= render 'use'
= render 'use'

View File

@ -4,7 +4,7 @@
- type_plural = _('project access tokens')
- @content_class = 'limit-container-width' unless fluid_layout
.row.gl-mt-3
.row.gl-mt-3.js-search-settings-section
.col-lg-4
%h4.gl-mt-0
= page_title

View File

@ -2,8 +2,9 @@
- breadcrumb_title _('Integration Settings')
- page_title _('Integrations')
%h3= _('Integrations')
- integrations_link_start = '<a href="%{url}">'.html_safe % { url: help_page_url('user/project/integrations/overview') }
- webhooks_link_start = '<a href="%{url}">'.html_safe % { url: project_hooks_path(@project) }
%p= _("%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}.").html_safe % { integrations_link_start: integrations_link_start, webhooks_link_start: webhooks_link_start, link_end: '</a>'.html_safe }
= render 'shared/integrations/index', integrations: @integrations
%section.js-search-settings-section
%h3= _('Integrations')
- integrations_link_start = '<a href="%{url}">'.html_safe % { url: help_page_url('user/project/integrations/overview') }
- webhooks_link_start = '<a href="%{url}">'.html_safe % { url: project_hooks_path(@project) }
%p= _("%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}.").html_safe % { integrations_link_start: integrations_link_start, webhooks_link_start: webhooks_link_start, link_end: '</a>'.html_safe }
= render 'shared/integrations/index', integrations: @integrations

View File

@ -1,6 +1,6 @@
- @content_class = "limit-container-width" unless fluid_layout
.row.gl-mt-3
.row.gl-mt-3.js-search-settings-section
.col-lg-4.profile-settings-sidebar
%h4.gl-mt-0
= page_title

View File

@ -0,0 +1,8 @@
---
name: improve_blobs_cache_headers
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98110
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/374126
milestone: '15.5'
type: development
group: group::source code
default_enabled: false

View File

@ -27,7 +27,7 @@ OmniAuth.config.full_host = Gitlab::OmniauthInitializer.full_host
OmniAuth.config.allowed_request_methods = [:post]
# In case of auto sign-in, the GET method is used (users don't get to click on a button)
OmniAuth.config.allowed_request_methods << :get if Gitlab.config.omniauth.auto_sign_in_with_provider.present?
OmniAuth.config.before_request_phase do |env|
OmniAuth.config.request_validation_phase do |env|
Gitlab::RequestForgeryProtection.call(env)
end

View File

@ -23,8 +23,6 @@ queues_config_hash[:namespace] = Gitlab::Redis::Queues::SIDEKIQ_NAMESPACE
enable_json_logs = Gitlab.config.sidekiq.log_format == 'json'
enable_sidekiq_memory_killer = ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS'].to_i.nonzero?
use_sidekiq_daemon_memory_killer = ENV.fetch("SIDEKIQ_DAEMON_MEMORY_KILLER", 1).to_i.nonzero?
use_sidekiq_legacy_memory_killer = !use_sidekiq_daemon_memory_killer
Sidekiq.configure_server do |config|
config.options[:strict] = false
@ -45,8 +43,7 @@ Sidekiq.configure_server do |config|
config.server_middleware(&Gitlab::SidekiqMiddleware.server_configurator(
metrics: Settings.monitoring.sidekiq_exporter,
arguments_logger: SidekiqLogArguments.enabled? && !enable_json_logs,
memory_killer: enable_sidekiq_memory_killer && use_sidekiq_legacy_memory_killer
arguments_logger: SidekiqLogArguments.enabled? && !enable_json_logs
))
config.client_middleware(&Gitlab::SidekiqMiddleware.client_configurator)
@ -62,7 +59,7 @@ Sidekiq.configure_server do |config|
# To cancel job, it requires `SIDEKIQ_MONITOR_WORKER=1` to enable notification channel
Gitlab::SidekiqDaemon::Monitor.instance.start
Gitlab::SidekiqDaemon::MemoryKiller.instance.start if enable_sidekiq_memory_killer && use_sidekiq_daemon_memory_killer
Gitlab::SidekiqDaemon::MemoryKiller.instance.start if enable_sidekiq_memory_killer
first_sidekiq_worker = !ENV['SIDEKIQ_WORKER_ID'] || ENV['SIDEKIQ_WORKER_ID'] == '0'
health_checks = Settings.monitoring.sidekiq_health_checks

View File

@ -40,7 +40,9 @@ if you want to implement this.
If you have installed GitLab from source:
1. You must [install Registry](https://docs.docker.com/registry/deploying/) by yourself.
1. You must [deploy a registry](https://docs.docker.com/registry/deploying/) using the image corresponding to the
version of GitLab you are installing
(for example: `registry.gitlab.com/gitlab-org/build/cng/gitlab-container-registry:v3.15.0-gitlab`)
1. After the installation is complete, to enable it, you must configure the Registry's
settings in `gitlab.yml`.
1. Use the sample NGINX configuration file from under

View File

@ -32,26 +32,12 @@ run as a process group leader (for example, using `chpst -P`). If using Omnibus
The MemoryKiller is controlled using environment variables.
- `SIDEKIQ_DAEMON_MEMORY_KILLER`: defaults to 1. When set to 0, the MemoryKiller
works in _legacy_ mode. Otherwise, the MemoryKiller works in _daemon_ mode.
In _legacy_ mode, the MemoryKiller checks the Sidekiq process RSS
([Resident Set Size](https://github.com/mperham/sidekiq/wiki/Memory#rss))
after each job.
In _daemon_ mode, the MemoryKiller checks the Sidekiq process RSS every 3 seconds
(defined by `SIDEKIQ_MEMORY_KILLER_CHECK_INTERVAL`).
- `SIDEKIQ_MEMORY_KILLER_MAX_RSS` (KB): if this variable is set, and its value is greater
than 0, the MemoryKiller is enabled. Otherwise the MemoryKiller is disabled.
`SIDEKIQ_MEMORY_KILLER_MAX_RSS` defines the Sidekiq process allowed RSS.
In _legacy_ mode, if the Sidekiq process exceeds the allowed RSS then an irreversible
delayed graceful restart is triggered. The restart of Sidekiq happens
after `SIDEKIQ_MEMORY_KILLER_GRACE_TIME` seconds.
In _daemon_ mode, if the Sidekiq process exceeds the allowed RSS for longer than
If the Sidekiq process exceeds the allowed RSS for longer than
`SIDEKIQ_MEMORY_KILLER_GRACE_TIME` the graceful restart is triggered. If the
Sidekiq process go below the allowed RSS within `SIDEKIQ_MEMORY_KILLER_GRACE_TIME`,
the restart is aborted.
@ -59,11 +45,11 @@ The MemoryKiller is controlled using environment variables.
The default value for Omnibus packages is set
[in the Omnibus GitLab repository](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-cookbooks/gitlab/attributes/default.rb).
- `SIDEKIQ_MEMORY_KILLER_HARD_LIMIT_RSS` (KB): is used by _daemon_ mode. If the Sidekiq
- `SIDEKIQ_MEMORY_KILLER_HARD_LIMIT_RSS` (KB): If the Sidekiq
process RSS (expressed in kilobytes) exceeds `SIDEKIQ_MEMORY_KILLER_HARD_LIMIT_RSS`,
an immediate graceful restart of Sidekiq is triggered.
- `SIDEKIQ_MEMORY_KILLER_CHECK_INTERVAL`: used in _daemon_ mode to define how
- `SIDEKIQ_MEMORY_KILLER_CHECK_INTERVAL`: Define how
often to check process RSS, default to 3 seconds.
- `SIDEKIQ_MEMORY_KILLER_GRACE_TIME`: defaults to 900 seconds (15 minutes).

View File

@ -15,6 +15,19 @@ We do not allow gems that are fetched from Git repositories. All gems have
to be available in the RubyGems index. We want to minimize external build
dependencies and build times.
## Review the new dependency for quality
We should not add 3rd-party dependencies to GitLab that would not pass our own quality standards.
This means that new dependencies should, at a minimum, meet the following criteria:
- They have an active developer community. At the minimum a maintainer should still be active
to merge change requests in case of emergencies.
- There are no issues open that we know may impact the availablity or performance of GitLab.
- The project is tested using some form of test automation. The test suite must be passing
using the Ruby version currently used by GitLab.
- If the project uses a C extension, consider requesting an additional review from a C or MRI
domain expert. C extensions can greatly impact GitLab stability and performance.
## Request an Appsec review
When adding a new gem to our `Gemfile` or even changing versions in

View File

@ -352,8 +352,6 @@ To create an [untarred](#skipping-tar-creation) incremental backup from a tarred
sudo gitlab-backup create INCREMENTAL=yes SKIP=tar
```
You can't create an incremental backup from an [untarred](#skipping-tar-creation) backup.
### Back up specific repository storages
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86896) in GitLab 15.0.

View File

@ -895,12 +895,11 @@ include:
merge cyclonedx sboms:
stage: merge-cyclonedx-sboms
image: alpine:latest
image:
name: cyclonedx/cyclonedx-cli:0.22.0
entrypoint: [""]
script:
- wget https://github.com/CycloneDX/cyclonedx-cli/releases/download/v0.22.0/cyclonedx-linux-musl-x64 -O /usr/local/bin/cyclonedx-cli
- chmod 755 /usr/local/bin/cyclonedx-cli
- apk --update add --no-cache icu-dev libstdc++
- find * -name "gl-sbom-*.cdx.json" -exec cyclonedx-cli merge --input-files {} --output-file gl-sbom-all.cdx.json +
- find . -name "gl-sbom-*.cdx.json" -exec /cyclonedx merge --output-file gl-sbom-all.cdx.json --input-files "{}" +
artifacts:
paths:
- gl-sbom-all.cdx.json

View File

@ -7,13 +7,13 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Migrating groups **(FREE)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/249160) in GitLab 13.7 for group resources [with a flag](../../feature_flags.md) named `bulk_import`. Disabled by default.
> - Group resources [enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/338985) in GitLab 14.3.
> - Group items [enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/338985) in GitLab 14.3.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267945) in GitLab 14.4 for project resources [with a flag](../../feature_flags.md) named `bulk_import_projects`. Disabled by default.
FLAG:
On self-managed GitLab, by default [migrating group resources](#migrated-group-resources) is available. To hide the
On self-managed GitLab, by default [migrating group items](#migrated-group-items) is available. To hide the
feature, ask an administrator to [disable the feature flag](../../../administration/feature_flags.md) named `bulk_import`.
On self-managed GitLab, by default [migrating project resources](#migrated-project-resources) is not available. To show
On self-managed GitLab, by default [migrating project items](#migrated-project-items) is not available. To show
this feature, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named
`bulk_import_projects`. On GitLab.com, migrating group resources is available but migrating project resources is not
available.
@ -33,8 +33,8 @@ another GitLab instance. Migrating groups using the method documented here autom
When you migrate a group, you connect to your GitLab instance and then choose
groups to import. Not all the data is migrated. See:
- [Migrated group resources](#migrated-group-resources).
- [Migrated project resources](#migrated-project-resources).
- [Migrated group items](#migrated-group-items).
- [Migrated project items](#migrated-project-items).
Leave feedback about group migration in [the relevant issue](https://gitlab.com/gitlab-org/gitlab/-/issues/284495).
@ -79,10 +79,17 @@ Migration importer page. The remote groups you have the Owner role for are liste
For information on automating user, group, and project import API calls, see
[Automate group and project import](../../project/import/index.md#automate-group-and-project-import).
## Migrated group resources
## Migrated group items
Only the following resources are migrated to the target instance. Any other items are **not**
migrated:
The [`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/group/import_export.yml)
file for groups lists many of the items migrated when migrating groups using group migration. View this file in the branch
for your version of GitLab to see the list of items relevant to you. For example,
[`import_export.yml` on the `14-10-stable-ee` branch](https://gitlab.com/gitlab-org/gitlab/-/blob/14-10-stable-ee/lib/gitlab/import_export/group/import_export.yml).
Migrating projects with file exports uses the same export and import mechanisms as creating projects from templates at the [group](../custom_project_templates.md) and
[instance](../../admin_area/custom_project_templates.md) levels. Therefore, the list of exported items is the same.
Items that are migrated to the target instance include:
- Badges ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292431) in 13.11)
- Board Lists
@ -105,13 +112,23 @@ migrated:
- Sub-Groups
- Uploads
## Migrated project resources
Any other items are **not** migrated.
## Migrated project items
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267945) in GitLab 14.4 [with a flag](../../feature_flags.md) named `bulk_import_projects`. Disabled by default.
FLAG:
On self-managed GitLab, migrating project resources are not available by default. To make them available, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `bulk_import_projects`. On GitLab.com, migrating project resources are not available.
The [`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/project/import_export.yml)
file for projects lists many of the items migrated when migrating projects using group migration. View this file in the branch
for your version of GitLab to see the list of items relevant to you. For example,
[`import_export.yml` on the `14-10-stable-ee` branch](https://gitlab.com/gitlab-org/gitlab/-/blob/14-10-stable-ee/lib/gitlab/import_export/project/import_export.yml).
Migrating projects with file exports uses the same export and import mechanisms as creating projects from templates at the [group](../../group/custom_project_templates.md) and
[instance](../../admin_area/custom_project_templates.md) levels. Therefore, the list of exported items is the same.
- Projects ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267945) in GitLab 14.4)
- Auto DevOps ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339410) in GitLab 14.6)
- Branches (including protected branches) ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339414) in GitLab 14.7)

View File

@ -48,7 +48,15 @@ sure these users exist before importing the desired groups.
### Exported contents
The following items are exported:
The [`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/group/import_export.yml)
file for groups lists many of the items exported and imported when migrating groups using file exports. View this file in the branch
for your version of GitLab to see the list of items relevant to you. For example,
[`import_export.yml` on the `14-10-stable-ee` branch](https://gitlab.com/gitlab-org/gitlab/-/blob/14-10-stable-ee/lib/gitlab/import_export/group/import_export.yml).
Migrating projects with file exports uses the same export and import mechanisms as creating projects from templates at the [group](../custom_project_templates.md) and
[instance](../../admin_area/custom_project_templates.md) levels. Therefore, the list of exported items is the same.
Items that are exported include:
- Milestones
- Labels
@ -62,7 +70,7 @@ The following items are exported:
([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53247) in GitLab 13.9)
- Iterations cadences ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95372) in 15.4)
The following items are **not** exported:
Items that are **not** exported include:
- Projects
- Runner tokens

View File

@ -58,7 +58,7 @@ moved to your configured `uploads_directory`. Every 24 hours, a worker deletes t
### Items that are exported
The [`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/project/import_export.yml)
file lists the items exported and imported when migrating projects using file exports. View this file in the branch
file for projects lists many of the items exported and imported when migrating projects using file exports. View this file in the branch
for your version of GitLab to see the list of items relevant to you. For example,
[`import_export.yml` on the `14-10-stable-ee` branch](https://gitlab.com/gitlab-org/gitlab/-/blob/14-10-stable-ee/lib/gitlab/import_export/project/import_export.yml).
@ -137,16 +137,15 @@ To import a project:
To get the status of an import, you can query it through the [Project import/export API](../../../api/project_import_export.md#import-status).
As described in the API documentation, the query may return an import error or exceptions.
### Items that are imported
### Changes to imported items
The following items are imported but changed slightly:
Exported items are imported with the following changes:
- Project members with the Owner role are imported as Maintainers.
- If an imported project contains merge requests originating from forks, then new branches
associated with such merge requests are created in a project during the import/export. Thus, the
number of branches in the exported project might be bigger than in the original project.
- If use of the `Internal` visibility level
[is restricted](../../public_access.md#restrict-use-of-public-or-internal-projects),
- Project members with the Owner role are imported with the Maintainer role.
- If an imported project contains merge requests originating from forks, new branches associated with these merge
requests are created in the project. Therefore, the number of branches in the new project can be more than in the
source project.
- If the `Internal` visibility level [is restricted](../../public_access.md#restrict-use-of-public-or-internal-projects),
all imported projects are given `Private` visibility.
Deploy keys aren't imported. To use deploy keys, you must enable them in your imported project and update protected branches.

View File

@ -7,7 +7,7 @@ module Gitlab
# The result of this method should be passed to
# Sidekiq's `config.server_middleware` method
# eg: `config.server_middleware(&Gitlab::SidekiqMiddleware.server_configurator)`
def self.server_configurator(metrics: true, arguments_logger: true, memory_killer: true)
def self.server_configurator(metrics: true, arguments_logger: true)
lambda do |chain|
# Size limiter should be placed at the top
chain.add ::Gitlab::SidekiqMiddleware::SizeLimiter::Server
@ -27,7 +27,6 @@ module Gitlab
end
chain.add ::Gitlab::SidekiqMiddleware::ArgumentsLogger if arguments_logger
chain.add ::Gitlab::SidekiqMiddleware::MemoryKiller if memory_killer
chain.add ::Gitlab::SidekiqMiddleware::RequestStoreMiddleware
chain.add ::Gitlab::SidekiqMiddleware::ExtraDoneLogMetadata
chain.add ::Gitlab::SidekiqMiddleware::BatchLoader

View File

@ -1,91 +0,0 @@
# frozen_string_literal: true
module Gitlab
module SidekiqMiddleware
class MemoryKiller
# Default the RSS limit to 0, meaning the MemoryKiller is disabled
MAX_RSS = (ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS'] || 0).to_s.to_i
# Give Sidekiq 15 minutes of grace time after exceeding the RSS limit
GRACE_TIME = (ENV['SIDEKIQ_MEMORY_KILLER_GRACE_TIME'] || 15 * 60).to_s.to_i
# Wait 30 seconds for running jobs to finish during graceful shutdown
SHUTDOWN_WAIT = (ENV['SIDEKIQ_MEMORY_KILLER_SHUTDOWN_WAIT'] || 30).to_s.to_i
# Create a mutex used to ensure there will be only one thread waiting to
# shut Sidekiq down
MUTEX = Mutex.new
attr_reader :worker
def call(worker, job, queue)
yield
@worker = worker
current_rss = get_rss
return unless MAX_RSS > 0 && current_rss > MAX_RSS
Thread.new do
# Return if another thread is already waiting to shut Sidekiq down
next unless MUTEX.try_lock
warn("Sidekiq worker PID-#{pid} current RSS #{current_rss}"\
" exceeds maximum RSS #{MAX_RSS} after finishing job #{worker.class} JID-#{job['jid']}")
warn("Sidekiq worker PID-#{pid} will stop fetching new jobs"\
" in #{GRACE_TIME} seconds, and will be shut down #{SHUTDOWN_WAIT} seconds later")
# Wait `GRACE_TIME` to give the memory intensive job time to finish.
# Then, tell Sidekiq to stop fetching new jobs.
wait_and_signal(GRACE_TIME, 'SIGTSTP', 'stop fetching new jobs')
# Wait `SHUTDOWN_WAIT` to give already fetched jobs time to finish.
# Then, tell Sidekiq to gracefully shut down by giving jobs a few more
# moments to finish, killing and requeuing them if they didn't, and
# then terminating itself. Sidekiq will replicate the TERM to all its
# children if it can.
wait_and_signal(SHUTDOWN_WAIT, 'SIGTERM', 'gracefully shut down')
# Wait for Sidekiq to shutdown gracefully, and kill it if it didn't.
# Kill the whole pgroup, so we can be sure no children are left behind
wait_and_signal_pgroup(Sidekiq.options[:timeout] + 2, 'SIGKILL', 'die')
end
end
private
def get_rss
output, status = Gitlab::Popen.popen(%W(ps -o rss= -p #{pid}), Rails.root.to_s)
return 0 unless status == 0
output.to_i
end
# If this sidekiq process is pgroup leader, signal to the whole pgroup
def wait_and_signal_pgroup(time, signal, explanation)
return wait_and_signal(time, signal, explanation) unless Process.getpgrp == pid
warn("waiting #{time} seconds before sending Sidekiq worker PGRP-#{pid} #{signal} (#{explanation})", signal: signal)
sleep(time)
warn("sending Sidekiq worker PGRP-#{pid} #{signal} (#{explanation})", signal: signal)
Process.kill(signal, 0)
end
def wait_and_signal(time, signal, explanation)
warn("waiting #{time} seconds before sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})", signal: signal)
sleep(time)
warn("sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})", signal: signal)
Process.kill(signal, pid)
end
def pid
Process.pid
end
def warn(message, signal: nil)
Sidekiq.logger.warn(class: worker.class.name, pid: pid, signal: signal, message: message)
end
end
end
end

View File

@ -14400,6 +14400,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
msgid "Edit your search and try again"
msgstr ""
msgid "Edit your search filter and try again."
msgstr ""

View File

@ -132,7 +132,7 @@ RSpec.describe Projects::DesignManagement::Designs::RawImagesController do
subject
expect(response.header['ETag']).to be_present
expect(response.header['Cache-Control']).to eq("max-age=60, private")
expect(response.header['Cache-Control']).to eq("max-age=60, private, must-revalidate, stale-while-revalidate=60, stale-if-error=300, s-maxage=60")
end
end

View File

@ -247,9 +247,11 @@ RSpec.describe Projects::RawController do
sign_in create(:user)
request_file
expect(response.cache_control[:public]).to eq(true)
expect(response.cache_control[:max_age]).to eq(60)
expect(response.headers['ETag']).to eq("\"bdd5aa537c1e1f6d1b66de4bac8a6132\"")
expect(response.cache_control[:no_store]).to be_nil
expect(response.header['Cache-Control']).to eq(
'max-age=60, public, must-revalidate, stale-while-revalidate=60, stale-if-error=300, s-maxage=60'
)
end
context 'when a public project has private repo' do
@ -260,7 +262,9 @@ RSpec.describe Projects::RawController do
sign_in user
request_file
expect(response.header['Cache-Control']).to include('max-age=60, private')
expect(response.header['Cache-Control']).to eq(
'max-age=60, private, must-revalidate, stale-while-revalidate=60, stale-if-error=300, s-maxage=60'
)
end
end
@ -274,6 +278,21 @@ RSpec.describe Projects::RawController do
expect(response).to have_gitlab_http_status(:not_modified)
end
end
context 'when improve_blobs_cache_headers disabled' do
before do
stub_feature_flags(improve_blobs_cache_headers: false)
end
it 'uses weak etags with a restricted set of headers' do
sign_in create(:user)
request_file
expect(response.headers['ETag']).to eq("W/\"bdd5aa537c1e1f6d1b66de4bac8a6132\"")
expect(response.cache_control[:no_store]).to be_nil
expect(response.header['Cache-Control']).to eq('max-age=60, public')
end
end
end
end
end

View File

@ -1,4 +1,5 @@
import { mount } from '@vue/test-utils';
import { GlLoadingIcon } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
import Vue, { nextTick } from 'vue';
import ContributorsCharts from '~/contributors/components/contributors.vue';
@ -52,14 +53,14 @@ describe('Contributors charts', () => {
it('should display loader whiled loading data', async () => {
wrapper.vm.$store.state.loading = true;
await nextTick();
expect(wrapper.find('.contributors-loader').exists()).toBe(true);
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
it('should render charts when loading completed and there is chart data', async () => {
wrapper.vm.$store.state.loading = false;
wrapper.vm.$store.state.chartData = chartData;
await nextTick();
expect(wrapper.find('.contributors-loader').exists()).toBe(false);
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
expect(wrapper.find('.contributors-charts').exists()).toBe(true);
expect(wrapper.element).toMatchSnapshot();
});

View File

@ -87,7 +87,7 @@ describe('Customer relations contacts root app', () => {
editButtonLabel: 'Edit',
title: 'Customer relations contacts',
newContact: 'New contact',
errorText: 'Something went wrong. Please try again.',
errorMsg: 'Something went wrong. Please try again.',
},
serverErrorMessage: '',
filterSearchKey: 'contacts',
@ -117,6 +117,18 @@ describe('Customer relations contacts root app', () => {
expect(wrapper.text()).toContain('Something went wrong. Please try again.');
});
it('should be removed on error-alert-dismissed event', async () => {
mountComponent({ queryHandler: jest.fn().mockRejectedValue('ERROR') });
await waitForPromises();
expect(wrapper.text()).toContain('Something went wrong. Please try again.');
findTable().vm.$emit('error-alert-dismissed');
await waitForPromises();
expect(wrapper.text()).not.toContain('Something went wrong. Please try again.');
});
});
describe('on successful load', () => {

View File

@ -91,7 +91,7 @@ describe('Customer relations organizations root app', () => {
editButtonLabel: 'Edit',
title: 'Customer relations organizations',
newOrganization: 'New organization',
errorText: 'Something went wrong. Please try again.',
errorMsg: 'Something went wrong. Please try again.',
},
serverErrorMessage: '',
filterSearchKey: 'organizations',

View File

@ -22,12 +22,15 @@ describe('Timeline events form', () => {
useFakeDate(fakeDate);
let wrapper;
const mountComponent = ({ mountMethod = shallowMountExtended }) => {
const mountComponent = ({ mountMethod = shallowMountExtended } = {}) => {
wrapper = mountMethod(TimelineEventsForm, {
propsData: {
showSaveAndAdd: true,
isEventProcessed: false,
},
stubs: {
GlButton: true,
},
});
};
@ -48,17 +51,18 @@ describe('Timeline events form', () => {
findHourInput().setValue(5);
findMinuteInput().setValue(45);
};
const findTextarea = () => wrapper.findByTestId('input-note');
const submitForm = async () => {
findSubmitButton().trigger('click');
findSubmitButton().vm.$emit('click');
await waitForPromises();
};
const submitFormAndAddAnother = async () => {
findSubmitAndAddButton().trigger('click');
findSubmitAndAddButton().vm.$emit('click');
await waitForPromises();
};
const cancelForm = async () => {
findCancelButton().trigger('click');
findCancelButton().vm.$emit('click');
await waitForPromises();
};
@ -118,5 +122,17 @@ describe('Timeline events form', () => {
expect(findHourInput().element.value).toBe('0');
expect(findMinuteInput().element.value).toBe('0');
});
it('should disable the save buttons when event content does not exist', async () => {
expect(findSubmitButton().props('disabled')).toBe(true);
expect(findSubmitAndAddButton().props('disabled')).toBe(true);
});
it('should enable the save buttons when event content exists', async () => {
await findTextarea().setValue('hello');
expect(findSubmitButton().props('disabled')).toBe(false);
expect(findSubmitAndAddButton().props('disabled')).toBe(false);
});
});
});

View File

@ -1,4 +1,4 @@
import { GlSearchBoxByType } from '@gitlab/ui';
import { GlEmptyState, GlSearchBoxByType } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { setHTMLFixture } from 'helpers/fixtures';
import SearchSettings from '~/search_settings/components/search_settings.vue';
@ -14,7 +14,7 @@ describe('search_settings/components/search_settings.vue', () => {
const EXTRA_SETTINGS_ID = 'js-extra-settings';
const TEXT_CONTAIN_SEARCH_TERM = `This text contain ${SEARCH_TERM}.`;
const TEXT_WITH_SIBLING_ELEMENTS = `${SEARCH_TERM} <a data-testid="sibling" href="#">Learn more</a>.`;
const HIDE_WHEN_EMPTY_CLASS = 'js-hide-when-nothing-matches-search';
let wrapper;
const buildWrapper = () => {
@ -22,6 +22,7 @@ describe('search_settings/components/search_settings.vue', () => {
propsData: {
searchRoot: document.querySelector(`#${ROOT_ID}`),
sectionSelector: SECTION_SELECTOR,
hideWhenEmptySelector: `.${HIDE_WHEN_EMPTY_CLASS}`,
isExpandedFn: isExpanded,
},
// Add real listeners so we can simplify and strengthen some tests.
@ -46,6 +47,8 @@ describe('search_settings/components/search_settings.vue', () => {
const findMatchSiblingElement = () => document.querySelector(`[data-testid="sibling"]`);
const findSearchBox = () => wrapper.find(GlSearchBoxByType);
const findEmptyState = () => wrapper.find(GlEmptyState);
const findHideWhenEmpty = () => document.querySelector(`.${HIDE_WHEN_EMPTY_CLASS}`);
const search = (term) => {
findSearchBox().vm.$emit('input', term);
};
@ -67,6 +70,9 @@ describe('search_settings/components/search_settings.vue', () => {
<span>${TEXT_CONTAIN_SEARCH_TERM}</span>
<span>${TEXT_WITH_SIBLING_ELEMENTS}</span>
</section>
<div class="row ${HIDE_WHEN_EMPTY_CLASS}">
<button type="submit">Save</button>
</div>
</div>
</div>
`);
@ -93,13 +99,41 @@ describe('search_settings/components/search_settings.vue', () => {
expect(wrapper.emitted('expand')).toEqual([[section]]);
});
describe('when nothing matches the search term', () => {
beforeEach(() => {
search('xxxxxxxxxxx');
});
it('shows an empty state', () => {
expect(findEmptyState().exists()).toBe(true);
});
it('hides the form buttons', () => {
expect(findHideWhenEmpty()).toHaveClass(HIDE_CLASS);
});
});
describe('when something matches the search term', () => {
beforeEach(() => {
search(SEARCH_TERM);
});
it('shows no empty state', () => {
expect(findEmptyState().exists()).toBe(false);
});
it('shows the form buttons', () => {
expect(findHideWhenEmpty()).not.toHaveClass(HIDE_CLASS);
});
});
it('highlight elements that match the search term', () => {
search(SEARCH_TERM);
expect(highlightedElementsCount()).toBe(3);
});
it('highlight only search term and not the whole line', () => {
it('highlights only search term and not the whole line', () => {
search(SEARCH_TERM);
expect(highlightedTextNodes()).toBe(true);
@ -142,6 +176,10 @@ describe('search_settings/components/search_settings.vue', () => {
expect(visibleSectionsCount()).toBe(sectionsCount());
});
it('hides the empty state', () => {
expect(findEmptyState().exists()).toBe(false);
});
it('removes the highlight from all elements', () => {
expect(highlightedElementsCount()).toBe(0);
});

View File

@ -1,83 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::SidekiqMiddleware::MemoryKiller do
subject { described_class.new }
let(:pid) { 999 }
let(:worker) { double(:worker, class: ProjectCacheWorker) }
let(:job) { { 'jid' => 123 } }
let(:queue) { 'test_queue' }
def run
thread = subject.call(worker, job, queue) { nil }
thread&.join
end
before do
allow(subject).to receive(:get_rss).and_return(10.kilobytes)
allow(subject).to receive(:pid).and_return(pid)
end
context 'when MAX_RSS is set to 0' do
before do
stub_const("#{described_class}::MAX_RSS", 0)
end
it 'does nothing' do
expect(subject).not_to receive(:sleep)
run
end
end
context 'when MAX_RSS is exceeded' do
before do
stub_const("#{described_class}::MAX_RSS", 5.kilobytes)
end
it 'sends the TSTP, TERM and KILL signals at expected times' do
expect(subject).to receive(:sleep).with(15 * 60).ordered
expect(Process).to receive(:kill).with('SIGTSTP', pid).ordered
expect(subject).to receive(:sleep).with(30).ordered
expect(Process).to receive(:kill).with('SIGTERM', pid).ordered
expect(subject).to receive(:sleep).with(Sidekiq.options[:timeout] + 2).ordered
expect(Process).to receive(:kill).with('SIGKILL', pid).ordered
expect(Sidekiq.logger)
.to receive(:warn).with(class: 'ProjectCacheWorker',
message: anything,
pid: pid,
signal: anything).at_least(:once)
run
end
it 'sends TSTP and TERM to the pid, but KILL to the pgroup, when running as process leader' do
allow(Process).to receive(:getpgrp) { pid }
allow(subject).to receive(:sleep)
expect(Process).to receive(:kill).with('SIGTSTP', pid).ordered
expect(Process).to receive(:kill).with('SIGTERM', pid).ordered
expect(Process).to receive(:kill).with('SIGKILL', 0).ordered
run
end
end
context 'when MAX_RSS is not exceeded' do
before do
stub_const("#{described_class}::MAX_RSS", 15.kilobytes)
end
it 'does nothing' do
expect(subject).not_to receive(:sleep)
run
end
end
end

View File

@ -322,8 +322,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics do
with_sidekiq_server_middleware do |chain|
Gitlab::SidekiqMiddleware.server_configurator(
metrics: true,
arguments_logger: false,
memory_killer: false
arguments_logger: false
).call(chain)
Sidekiq::Testing.inline! { example.run }

View File

@ -60,7 +60,6 @@ RSpec.describe Gitlab::SidekiqMiddleware do
::Labkit::Middleware::Sidekiq::Server,
::Gitlab::SidekiqMiddleware::ServerMetrics,
::Gitlab::SidekiqMiddleware::ArgumentsLogger,
::Gitlab::SidekiqMiddleware::MemoryKiller,
::Gitlab::SidekiqMiddleware::RequestStoreMiddleware,
::Gitlab::SidekiqMiddleware::ExtraDoneLogMetadata,
::Gitlab::SidekiqMiddleware::BatchLoader,
@ -79,8 +78,7 @@ RSpec.describe Gitlab::SidekiqMiddleware do
with_sidekiq_server_middleware do |chain|
described_class.server_configurator(
metrics: true,
arguments_logger: true,
memory_killer: true
arguments_logger: true
).call(chain)
Sidekiq::Testing.inline! { example.run }
@ -112,16 +110,14 @@ RSpec.describe Gitlab::SidekiqMiddleware do
let(:configurator) do
described_class.server_configurator(
metrics: false,
arguments_logger: false,
memory_killer: false
arguments_logger: false
)
end
let(:disabled_sidekiq_middlewares) do
[
Gitlab::SidekiqMiddleware::ServerMetrics,
Gitlab::SidekiqMiddleware::ArgumentsLogger,
Gitlab::SidekiqMiddleware::MemoryKiller
Gitlab::SidekiqMiddleware::ArgumentsLogger
]
end

View File

@ -407,8 +407,7 @@ RSpec.configure do |config|
with_sidekiq_server_middleware do |chain|
Gitlab::SidekiqMiddleware.server_configurator(
metrics: false, # The metrics don't go anywhere in tests
arguments_logger: false, # We're not logging the regular messages for inline jobs
memory_killer: false # This is not a thing we want to do inline in tests
arguments_logger: false # We're not logging the regular messages for inline jobs
).call(chain)
chain.add DisableQueryLimit
chain.insert_after ::Gitlab::SidekiqMiddleware::RequestStoreMiddleware, IsolatedRequestStore

View File

@ -300,7 +300,7 @@ RSpec.shared_examples 'wiki controller actions' do
expect(response.headers['Content-Disposition']).to match(/^inline/)
expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq('true')
expect(response.cache_control[:public]).to be(false)
expect(response.headers['Cache-Control']).to eq('max-age=60, private')
expect(response.headers['Cache-Control']).to eq('max-age=60, private, must-revalidate, stale-while-revalidate=60, stale-if-error=300, s-maxage=60')
end
end
end