Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
89db068a0a
commit
0b84d7017e
|
|
@ -275,6 +275,14 @@ Dangerfile
|
|||
/ee/spec/services/software_license_policies/**
|
||||
/spec/finders/security/license_compliance_jobs_finder_spec.rb
|
||||
|
||||
^[Secure::Static Analysis] @gitlab-org/secure/static-analysis
|
||||
/ee/lib/gitlab/checks/secrets_check.rb
|
||||
/ee/spec/lib/gitlab/checks/secrets_check_spec.rb
|
||||
/ee/spec/support/shared_contexts/secrets_check_shared_contexts.rb
|
||||
/ee/spec/support/shared_examples/lib/gitlab/secrets_check_shared_examples.rb
|
||||
/lib/gitlab/checks/changed_blobs.rb
|
||||
/spec/lib/gitlab/checks/changed_blobs.rb
|
||||
|
||||
^[Code Owners] @reprazent @kerrizor @garyh
|
||||
/ee/lib/gitlab/code_owners.rb
|
||||
/ee/lib/gitlab/code_owners/
|
||||
|
|
|
|||
|
|
@ -3703,6 +3703,7 @@ Layout/LineLength:
|
|||
- 'spec/lib/gitlab/import_export/uploads_manager_spec.rb'
|
||||
- 'spec/lib/gitlab/import_export/version_checker_spec.rb'
|
||||
- 'spec/lib/gitlab/import_sources_spec.rb'
|
||||
- 'spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb'
|
||||
- 'spec/lib/gitlab/issuable_metadata_spec.rb'
|
||||
- 'spec/lib/gitlab/issues/rebalancing/state_spec.rb'
|
||||
- 'spec/lib/gitlab/jira/dvcs_spec.rb'
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ Lint/AmbiguousOperatorPrecedence:
|
|||
- 'spec/lib/gitlab/database/batch_count_spec.rb'
|
||||
- 'spec/lib/gitlab/database/consistency_checker_spec.rb'
|
||||
- 'spec/lib/gitlab/graphql/tracers/metrics_tracer_spec.rb'
|
||||
- 'spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb'
|
||||
- 'spec/lib/gitlab/issues/rebalancing/state_spec.rb'
|
||||
- 'spec/lib/gitlab/kroki_spec.rb'
|
||||
- 'spec/lib/gitlab/memory/instrumentation_spec.rb'
|
||||
|
|
|
|||
|
|
@ -3587,6 +3587,7 @@ RSpec/FeatureCategory:
|
|||
- 'spec/lib/gitlab/instrumentation/rate_limiting_gates_spec.rb'
|
||||
- 'spec/lib/gitlab/instrumentation/redis_base_spec.rb'
|
||||
- 'spec/lib/gitlab/instrumentation/redis_cluster_validator_spec.rb'
|
||||
- 'spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb'
|
||||
- 'spec/lib/gitlab/instrumentation/redis_spec.rb'
|
||||
- 'spec/lib/gitlab/internal_post_receive/response_spec.rb'
|
||||
- 'spec/lib/gitlab/issuable/clone/attributes_rewriter_spec.rb'
|
||||
|
|
@ -5323,7 +5324,6 @@ RSpec/FeatureCategory:
|
|||
- 'spec/views/layouts/devise_empty.html.haml_spec.rb'
|
||||
- 'spec/views/layouts/fullscreen.html.haml_spec.rb'
|
||||
- 'spec/views/layouts/profile.html.haml_spec.rb'
|
||||
- 'spec/views/layouts/signup_onboarding.html.haml_spec.rb'
|
||||
- 'spec/views/layouts/terms.html.haml_spec.rb'
|
||||
- 'spec/views/notify/approved_merge_request_email.html.haml_spec.rb'
|
||||
- 'spec/views/notify/autodevops_disabled_email.text.erb_spec.rb'
|
||||
|
|
|
|||
|
|
@ -2332,6 +2332,7 @@ RSpec/NamedSubject:
|
|||
- 'spec/lib/gitlab/rack_attack_spec.rb'
|
||||
- 'spec/lib/gitlab/reactive_cache_set_cache_spec.rb'
|
||||
- 'spec/lib/gitlab/redis/boolean_spec.rb'
|
||||
- 'spec/lib/gitlab/redis/cross_slot_spec.rb'
|
||||
- 'spec/lib/gitlab/redis/db_load_balancing_spec.rb'
|
||||
- 'spec/lib/gitlab/redis/multi_store_spec.rb'
|
||||
- 'spec/lib/gitlab/redis/queues_spec.rb'
|
||||
|
|
|
|||
|
|
@ -2517,6 +2517,7 @@ Style/InlineDisableAnnotation:
|
|||
- 'lib/gitlab/import_export/project/relation_factory.rb'
|
||||
- 'lib/gitlab/import_sources.rb'
|
||||
- 'lib/gitlab/instrumentation/redis_cluster_validator.rb'
|
||||
- 'lib/gitlab/instrumentation/redis_interceptor.rb'
|
||||
- 'lib/gitlab/internal_events.rb'
|
||||
- 'lib/gitlab/issuable/clone/copy_resource_events_service.rb'
|
||||
- 'lib/gitlab/issues/rebalancing/state.rb'
|
||||
|
|
@ -2555,6 +2556,7 @@ Style/InlineDisableAnnotation:
|
|||
- 'lib/gitlab/pagination/offset_pagination.rb'
|
||||
- 'lib/gitlab/pagination_delegate.rb'
|
||||
- 'lib/gitlab/patch/action_cable_subscription_adapter_identifier.rb'
|
||||
- 'lib/gitlab/patch/node_loader.rb'
|
||||
- 'lib/gitlab/patch/prependable.rb'
|
||||
- 'lib/gitlab/patch/redis_cache_store.rb'
|
||||
- 'lib/gitlab/patch/sidekiq_cron_poller.rb'
|
||||
|
|
@ -2571,6 +2573,7 @@ Style/InlineDisableAnnotation:
|
|||
- 'lib/gitlab/rack_attack.rb'
|
||||
- 'lib/gitlab/rack_attack/request.rb'
|
||||
- 'lib/gitlab/rack_attack/store.rb'
|
||||
- 'lib/gitlab/redis/cross_slot.rb'
|
||||
- 'lib/gitlab/redis/hll.rb'
|
||||
- 'lib/gitlab/redis/multi_store.rb'
|
||||
- 'lib/gitlab/reference_extractor.rb'
|
||||
|
|
@ -2939,7 +2942,9 @@ Style/InlineDisableAnnotation:
|
|||
- 'spec/lib/gitlab/pagination/keyset/order_spec.rb'
|
||||
- 'spec/lib/gitlab/pagination/keyset/simple_order_builder_spec.rb'
|
||||
- 'spec/lib/gitlab/patch/database_config_spec.rb'
|
||||
- 'spec/lib/gitlab/patch/node_loader_spec.rb'
|
||||
- 'spec/lib/gitlab/quick_actions/dsl_spec.rb'
|
||||
- 'spec/lib/gitlab/redis/cross_slot_spec.rb'
|
||||
- 'spec/lib/gitlab/redis/multi_store_spec.rb'
|
||||
- 'spec/lib/gitlab/search/abuse_detection_spec.rb'
|
||||
- 'spec/lib/gitlab/shard_health_cache_spec.rb'
|
||||
|
|
|
|||
7
Gemfile
7
Gemfile
|
|
@ -288,9 +288,8 @@ gem 'js_regex', '~> 3.8' # rubocop:todo Gemfile/MissingFeatureCategory
|
|||
gem 'device_detector' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
|
||||
# Redis
|
||||
gem 'redis-namespace', '~> 1.10.0', feature_category: :redis
|
||||
gem 'redis', '~> 5.0.0', feature_category: :redis
|
||||
gem 'redis-clustering', '~> 5.0.0', feature_category: :redis
|
||||
gem 'redis', '~> 4.8.0' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
gem 'redis-namespace', '~> 1.10.0' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
gem 'connection_pool', '~> 2.4' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
|
||||
# Redis session store
|
||||
|
|
@ -512,7 +511,7 @@ group :test do
|
|||
|
||||
gem 'shoulda-matchers', '~> 5.1.0', require: false # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
gem 'email_spec', '~> 2.2.0' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
gem 'webmock', '~> 3.19.1' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
gem 'webmock', '~> 3.20.0' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
gem 'rails-controller-testing' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
gem 'concurrent-ruby', '~> 1.1' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
gem 'test-prof', '~> 1.3.1' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
|
|
|
|||
|
|
@ -523,11 +523,9 @@
|
|||
{"name":"recaptcha","version":"5.12.3","platform":"ruby","checksum":"37d1894add9e70a54d0c6c7f0ecbeedffbfa7d075acfbd4c509818dfdebdb7ee"},
|
||||
{"name":"recursive-open-struct","version":"1.1.3","platform":"ruby","checksum":"a3538a72552fcebcd0ada657bdff313641a4a5fbc482c08cfb9a65acb1c9de5a"},
|
||||
{"name":"redcarpet","version":"3.6.0","platform":"ruby","checksum":"8ad1889c0355ff4c47174af14edd06d62f45a326da1da6e8a121d59bdcd2e9e9"},
|
||||
{"name":"redis","version":"5.0.8","platform":"ruby","checksum":"3b770ea597850b26d6a9718fa184241e53e6c8a7ae0486ee8bfaefd29f26f3d8"},
|
||||
{"name":"redis","version":"4.8.0","platform":"ruby","checksum":"2000cf5014669c9dc821704b6d322a35a9a33852a95208911d9175d63b448a44"},
|
||||
{"name":"redis-actionpack","version":"5.4.0","platform":"ruby","checksum":"f10cf649ab05914716d63334d7f709221ecc883b87cf348f90ecfe0c35ea3540"},
|
||||
{"name":"redis-client","version":"0.19.0","platform":"ruby","checksum":"6ed9af23ff5aa87cf4d59439db77082b4cae5a0abbdd114ec5420bd63456324d"},
|
||||
{"name":"redis-cluster-client","version":"0.7.5","platform":"ruby","checksum":"12fd1c9eda17157a5cd2ce46afba13a024c28d24922092299a8daa9f46e4e78a"},
|
||||
{"name":"redis-clustering","version":"5.0.8","platform":"ruby","checksum":"8e2f3de3b1a700668eeac59125636e01be6ecd985e635a4d5649c47d71f6e166"},
|
||||
{"name":"redis-namespace","version":"1.10.0","platform":"ruby","checksum":"2c1c6ea7c6c5e343e75b9bee3aa4c265e364a5b9966507397467af2bb3758d94"},
|
||||
{"name":"redis-rack","version":"3.0.0","platform":"ruby","checksum":"abb50b82ae10ad4d11ca2e4901bfc2b98256cdafbbd95f80c86fc9e001478380"},
|
||||
{"name":"redis-store","version":"1.10.0","platform":"ruby","checksum":"f258894f9f7e82834308a3d86242294f0cff2c9db9ae66e5cb4c553a5ec8b09e"},
|
||||
|
|
@ -710,7 +708,7 @@
|
|||
{"name":"warning","version":"1.3.0","platform":"ruby","checksum":"23695a5d8e50bd5c46068931b529bee0b28e4982cbcefbe77d867800dde8069e"},
|
||||
{"name":"webauthn","version":"3.0.0","platform":"ruby","checksum":"3f77d422c2a8a4b31e56cf42f83414bd066e0506e9896936e1730262dc4a20e6"},
|
||||
{"name":"webfinger","version":"1.2.0","platform":"ruby","checksum":"7814ef1c85da47514f65c6e5ca14205fa9ce41ea2a70785e0c872842162852a2"},
|
||||
{"name":"webmock","version":"3.19.1","platform":"ruby","checksum":"eae7eee33989478188451f1fda4224d7fbe097c5c14e96b40b57347ef2d5d16d"},
|
||||
{"name":"webmock","version":"3.20.0","platform":"ruby","checksum":"cc33eaacc0fcfb5b36a127949557ab7089d4249b03a7cb0215dbc40e617a8365"},
|
||||
{"name":"webrick","version":"1.8.1","platform":"ruby","checksum":"19411ec6912911fd3df13559110127ea2badd0c035f7762873f58afc803e158f"},
|
||||
{"name":"websocket","version":"1.2.10","platform":"ruby","checksum":"2cc1a4a79b6e63637b326b4273e46adcddf7871caa5dc5711f2ca4061a629fa8"},
|
||||
{"name":"websocket-driver","version":"0.7.6","platform":"java","checksum":"bc894b9e9d5aee55ac04b61003e1957c4ef411a5a048199587d0499785b505c3"},
|
||||
|
|
|
|||
15
Gemfile.lock
15
Gemfile.lock
|
|
@ -1385,19 +1385,13 @@ GEM
|
|||
json
|
||||
recursive-open-struct (1.1.3)
|
||||
redcarpet (3.6.0)
|
||||
redis (5.0.8)
|
||||
redis-client (>= 0.17.0)
|
||||
redis (4.8.0)
|
||||
redis-actionpack (5.4.0)
|
||||
actionpack (>= 5, < 8)
|
||||
redis-rack (>= 2.1.0, < 4)
|
||||
redis-store (>= 1.1.0, < 2)
|
||||
redis-client (0.19.0)
|
||||
connection_pool
|
||||
redis-cluster-client (0.7.5)
|
||||
redis-client (~> 0.12)
|
||||
redis-clustering (5.0.8)
|
||||
redis (= 5.0.8)
|
||||
redis-cluster-client (>= 0.7.0)
|
||||
redis-namespace (1.10.0)
|
||||
redis (>= 4)
|
||||
redis-rack (3.0.0)
|
||||
|
|
@ -1796,7 +1790,7 @@ GEM
|
|||
webfinger (1.2.0)
|
||||
activesupport
|
||||
httpclient (>= 2.4)
|
||||
webmock (3.19.1)
|
||||
webmock (3.20.0)
|
||||
addressable (>= 2.8.0)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
|
|
@ -2075,9 +2069,8 @@ DEPENDENCIES
|
|||
rbtrace (~> 0.4)
|
||||
re2 (= 2.7.0)
|
||||
recaptcha (~> 5.12)
|
||||
redis (~> 5.0.0)
|
||||
redis (~> 4.8.0)
|
||||
redis-actionpack (~> 5.4.0)
|
||||
redis-clustering (~> 5.0.0)
|
||||
redis-namespace (~> 1.10.0)
|
||||
request_store (~> 1.5.1)
|
||||
responders (~> 3.0)
|
||||
|
|
@ -2153,7 +2146,7 @@ DEPENDENCIES
|
|||
vmstat (~> 2.3.0)
|
||||
warning (~> 1.3.0)
|
||||
webauthn (~> 3.0)
|
||||
webmock (~> 3.19.1)
|
||||
webmock (~> 3.20.0)
|
||||
webrick (~> 1.8.1)
|
||||
wikicloth (= 0.8.1)
|
||||
yajl-ruby (~> 1.4.3)
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export const emptyStateIllustration = (state) => state?.job?.status?.illustratio
|
|||
export const emptyStateAction = (state) => state?.job?.status?.action || null;
|
||||
|
||||
/**
|
||||
* Shared runners limit is only rendered when
|
||||
* Instance/shared runners limit is only rendered when
|
||||
* used quota is bigger or equal than the limit
|
||||
*
|
||||
* @returns {Boolean}
|
||||
|
|
|
|||
|
|
@ -127,13 +127,13 @@ export default {
|
|||
:value="sharedRunnersToggleValue"
|
||||
:is-loading="isLoading"
|
||||
:disabled="isSharedRunnersToggleDisabled"
|
||||
:label="__('Enable shared runners for this group')"
|
||||
:description="__('Enable shared runners for all projects and subgroups in this group.')"
|
||||
:label="__('Enable instance runners for this group')"
|
||||
:description="__('Enable instance runners for all projects and subgroups in this group.')"
|
||||
data-testid="shared-runners-toggle"
|
||||
@change="onSharedRunnersToggle"
|
||||
>
|
||||
<template v-if="isSharedRunnersToggleDisabled" #help>
|
||||
{{ s__('Runners|Shared runners are disabled.') }}
|
||||
{{ s__('Runners|Instance runners are disabled.') }}
|
||||
<gl-sprintf
|
||||
v-if="isParentAvailable"
|
||||
:message="s__('Runners|Go to %{groupLink} to enable them.')"
|
||||
|
|
@ -159,7 +159,7 @@ export default {
|
|||
@change="onOverrideToggle"
|
||||
>
|
||||
<template v-if="isSharedRunnersToggleDisabled" #help>
|
||||
{{ s__('Runners|Shared runners are disabled.') }}
|
||||
{{ s__('Runners|Instance runners are disabled.') }}
|
||||
<gl-sprintf
|
||||
v-if="isParentAvailable"
|
||||
:message="s__('Runners|Go to %{groupLink} to enable them.')"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { __, s__ } from '~/locale';
|
||||
|
||||
export const I18N_CONFIRM_MESSAGE = s__(
|
||||
'Runners|Shared runners will be disabled for all projects and subgroups in this group.',
|
||||
'Runners|Instance runners will be disabled for all projects and subgroups in this group.',
|
||||
);
|
||||
export const I18N_CONFIRM_OK = s__('Runners|Yes, disable shared runners');
|
||||
export const I18N_CONFIRM_CANCEL = s__('Runners|No, keep shared runners enabled');
|
||||
export const I18N_CONFIRM_OK = s__('Runners|Yes, disable instance runners');
|
||||
export const I18N_CONFIRM_CANCEL = s__('Runners|No, keep instance runners enabled');
|
||||
export const I18N_CONFIRM_TITLE = s__(
|
||||
'Runners|Are you sure you want to disable shared runners for %{groupName}?',
|
||||
'Runners|Are you sure you want to disable instance runners for %{groupName}?',
|
||||
);
|
||||
|
||||
export const I18N_UPDATE_ERROR_MESSAGE = __('An error occurred while updating configuration.');
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { CC_VALIDATION_REQUIRED_ERROR } from '../constants';
|
|||
|
||||
const DEFAULT_ERROR_MESSAGE = __('An error occurred while updating the configuration.');
|
||||
const REQUIRES_VALIDATION_TEXT = s__(
|
||||
`Billings|Shared runners cannot be enabled until a valid credit card is on file.`,
|
||||
`Billings|Instance runners cannot be enabled until a valid credit card is on file.`,
|
||||
);
|
||||
|
||||
export default {
|
||||
|
|
@ -122,13 +122,13 @@ export default {
|
|||
ref="sharedRunnersToggle"
|
||||
:disabled="isDisabledAndUnoverridable"
|
||||
:is-loading="isLoading"
|
||||
:label="__('Enable shared runners for this project')"
|
||||
:label="__('Enable instance runners for this project')"
|
||||
:value="isSharedRunnerEnabled"
|
||||
data-testid="toggle-shared-runners"
|
||||
@change="toggleSharedRunners"
|
||||
>
|
||||
<template v-if="isDisabledAndUnoverridable" #help>
|
||||
{{ s__('Runners|Shared runners are disabled in the group settings.') }}
|
||||
{{ s__('Runners|Instance runners are disabled in the group settings.') }}
|
||||
<gl-sprintf
|
||||
v-if="isGroupSettingsAvailable"
|
||||
:message="s__('Runners|Go to %{groupLink} to enable them.')"
|
||||
|
|
|
|||
|
|
@ -17,5 +17,5 @@ export const ACCESS_LEVEL_NONE = 0;
|
|||
|
||||
// must match shared_runners_setting in update_service.rb
|
||||
export const CC_VALIDATION_REQUIRED_ERROR = __(
|
||||
'Shared runners enabled cannot be enabled until a valid credit card is on file',
|
||||
'Instance runners enabled cannot be enabled until a valid credit card is on file',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -442,3 +442,11 @@
|
|||
color: $gl-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin devise-errors {
|
||||
h2 {
|
||||
margin-top: 0;
|
||||
font-size: $gl-font-size;
|
||||
color: var(--red-700, $red-700);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,23 @@
|
|||
@import 'mixins_and_variables_and_functions';
|
||||
|
||||
.html-devise-layout {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
|
||||
body {
|
||||
&.with-system-header {
|
||||
padding-top: $system-header-height;
|
||||
}
|
||||
|
||||
&.with-system-footer {
|
||||
.footer-container {
|
||||
padding-bottom: $system-footer-height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Login Page */
|
||||
.login-page {
|
||||
.container {
|
||||
|
|
@ -42,30 +60,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
.devise-errors {
|
||||
h2 {
|
||||
margin-top: 0;
|
||||
font-size: 14px;
|
||||
color: $red-700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.html-devise-layout {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
|
||||
body {
|
||||
&.with-system-header {
|
||||
padding-top: $system-header-height;
|
||||
}
|
||||
|
||||
&.with-system-footer {
|
||||
.footer-container {
|
||||
padding-bottom: $system-footer-height;
|
||||
}
|
||||
}
|
||||
@include devise-errors;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,6 @@
|
|||
@import 'mixins_and_variables_and_functions';
|
||||
|
||||
.signup-page {
|
||||
.devise-errors {
|
||||
h2 {
|
||||
font-size: $gl-font-size;
|
||||
color: var(--red-700, $red-700);
|
||||
}
|
||||
}
|
||||
|
||||
.decline-page {
|
||||
width: 350px;
|
||||
}
|
||||
|
|
@ -16,3 +9,7 @@
|
|||
.edit-profile {
|
||||
max-width: 460px;
|
||||
}
|
||||
|
||||
.devise-errors {
|
||||
@include devise-errors;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class InvitesController < ApplicationController
|
|||
|
||||
def decline
|
||||
if member.decline_invite!
|
||||
return render layout: 'signup_onboarding' if !current_user && member.invite_to_unknown_user? && member.created_by
|
||||
return render layout: 'minimal' if !current_user && member.invite_to_unknown_user? && member.created_by
|
||||
|
||||
path =
|
||||
if current_user
|
||||
|
|
|
|||
|
|
@ -16,8 +16,14 @@ module Mutations
|
|||
argument :full_path, GraphQL::Types::ID,
|
||||
required: true,
|
||||
description: 'Full path of the group that will be updated.'
|
||||
argument :lock_math_rendering_limits_enabled, GraphQL::Types::Boolean,
|
||||
required: false,
|
||||
description: copy_field_description(Types::GroupType, :lock_math_rendering_limits_enabled)
|
||||
argument :math_rendering_limits_enabled, GraphQL::Types::Boolean,
|
||||
required: false,
|
||||
description: copy_field_description(Types::GroupType, :math_rendering_limits_enabled)
|
||||
argument :shared_runners_setting, Types::Namespace::SharedRunnersSettingEnum,
|
||||
required: true,
|
||||
required: false,
|
||||
description: copy_field_description(Types::GroupType, :shared_runners_setting)
|
||||
|
||||
def resolve(full_path:, **args)
|
||||
|
|
|
|||
|
|
@ -311,6 +311,18 @@ module Types
|
|||
resolver: Resolvers::AutocompleteUsersResolver,
|
||||
description: 'Search users for autocompletion'
|
||||
|
||||
field :lock_math_rendering_limits_enabled,
|
||||
GraphQL::Types::Boolean,
|
||||
null: true,
|
||||
method: :lock_math_rendering_limits_enabled?,
|
||||
description: 'Indicates if math rendering limits are locked for all descendant groups.'
|
||||
|
||||
field :math_rendering_limits_enabled,
|
||||
GraphQL::Types::Boolean,
|
||||
null: true,
|
||||
method: :math_rendering_limits_enabled?,
|
||||
description: 'Indicates if math rendering limits are used for this group.'
|
||||
|
||||
def label(title:)
|
||||
BatchLoader::GraphQL.for(title).batch(key: group) do |titles, loader, args|
|
||||
LabelsFinder
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ class ActiveSession
|
|||
)
|
||||
|
||||
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
|
||||
redis.pipelined do |pipeline|
|
||||
Gitlab::Redis::CrossSlot::Pipeline.new(redis).pipelined do |pipeline|
|
||||
pipeline.setex(
|
||||
key_name(user.id, session_private_id),
|
||||
expiry,
|
||||
|
|
|
|||
|
|
@ -157,6 +157,12 @@ class Namespace < ApplicationRecord
|
|||
:npm_package_requests_forwarding,
|
||||
to: :package_settings
|
||||
delegate :default_branch_protection_defaults, to: :namespace_settings, allow_nil: true
|
||||
delegate :math_rendering_limits_enabled,
|
||||
:lock_math_rendering_limits_enabled,
|
||||
to: :namespace_settings, allow_nil: true
|
||||
delegate :math_rendering_limits_enabled?,
|
||||
:lock_math_rendering_limits_enabled?,
|
||||
to: :namespace_settings
|
||||
|
||||
before_save :update_new_emails_created_column, if: -> { emails_disabled_changed? }
|
||||
before_create :sync_share_with_group_lock_with_parent
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ class NamespaceSetting < ApplicationRecord
|
|||
|
||||
cascading_attr :toggle_security_policy_custom_ci
|
||||
cascading_attr :toggle_security_policies_policy_scope
|
||||
cascading_attr :math_rendering_limits_enabled
|
||||
|
||||
belongs_to :namespace, inverse_of: :namespace_settings
|
||||
|
||||
|
|
@ -50,6 +51,8 @@ class NamespaceSetting < ApplicationRecord
|
|||
subgroup_runner_token_expiration_interval
|
||||
project_runner_token_expiration_interval
|
||||
default_branch_protection_defaults
|
||||
math_rendering_limits_enabled
|
||||
lock_math_rendering_limits_enabled
|
||||
].freeze
|
||||
|
||||
# matches the size set in the database constraint
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module BranchRules
|
||||
class BaseService
|
||||
include Gitlab::Allowable
|
||||
|
||||
attr_accessor :project, :branch_rule, :current_user
|
||||
|
||||
def initialize(branch_rule, user = nil)
|
||||
@branch_rule = branch_rule
|
||||
@project = branch_rule.project
|
||||
@current_user = user
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
BranchRules::BaseService.prepend_mod
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module BranchRules
|
||||
class DestroyService < BaseService
|
||||
def execute
|
||||
raise Gitlab::Access::AccessDeniedError unless can_destroy_branch_rule?
|
||||
|
||||
return destroy_protected_branch if branch_rule.instance_of?(Projects::BranchRule)
|
||||
|
||||
yield if block_given?
|
||||
|
||||
ServiceResponse.error(message: 'Unknown branch rule type.')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def can_destroy_branch_rule?
|
||||
can?(current_user, :destroy_protected_branch, branch_rule)
|
||||
end
|
||||
|
||||
def destroy_protected_branch
|
||||
service = ProtectedBranches::DestroyService.new(project, current_user)
|
||||
|
||||
return ServiceResponse.success if service.execute(branch_rule.protected_branch)
|
||||
|
||||
ServiceResponse.error(message: 'Failed to delete branch rule.')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
BranchRules::DestroyService.prepend_mod
|
||||
|
|
@ -74,6 +74,8 @@ module Groups
|
|||
end
|
||||
|
||||
params.delete(:allow_mfa_for_subgroups)
|
||||
params.delete(:math_rendering_limits_enabled)
|
||||
params.delete(:lock_math_rendering_limits_enabled)
|
||||
end
|
||||
|
||||
def create_chat_team?
|
||||
|
|
|
|||
|
|
@ -141,6 +141,11 @@ module Groups
|
|||
params.delete(:default_branch_protection)
|
||||
params.delete(:default_branch_protection_defaults)
|
||||
end
|
||||
|
||||
unless can?(current_user, :admin_namespace, group)
|
||||
params.delete(:math_rendering_limits_enabled)
|
||||
params.delete(:lock_math_rendering_limits_enabled)
|
||||
end
|
||||
end
|
||||
|
||||
def handle_changes
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@
|
|||
= link_to _('Learn more.'), help_page_path('topics/autodevops/stages', anchor: 'auto-review-apps'), target: '_blank', rel: 'noopener noreferrer'
|
||||
|
||||
.form-group
|
||||
= f.gitlab_ui_checkbox_component :shared_runners_enabled, s_("AdminSettings|Enable shared runners for new projects"), help_text: s_("AdminSettings|All new projects can use the instance's shared runners by default.")
|
||||
= f.gitlab_ui_checkbox_component :shared_runners_enabled, s_("AdminSettings|Enable instance runners for new projects"), help_text: s_("AdminSettings|All new projects can use instance runners by default.")
|
||||
|
||||
= render_if_exists 'admin/application_settings/shared_runners_minutes_setting', form: f
|
||||
|
||||
.form-group
|
||||
= f.label :shared_runners_text, _('Shared runners details'), class: 'label-bold'
|
||||
= f.label :shared_runners_text, s_('AdminSettings|Instance runners details'), class: 'label-bold'
|
||||
= f.text_area :shared_runners_text, class: 'form-control gl-form-input', rows: 4
|
||||
.form-text.text-muted= _("Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported.")
|
||||
.form-text.text-muted= s_('AdminSettings|Add a custom message with details about instance runners. The message is visible when you view runners for projects and groups. Markdown is supported.')
|
||||
.form-group
|
||||
= f.label :max_artifacts_size, _('Maximum artifacts size (MB)'), class: 'label-bold'
|
||||
= f.number_field :max_artifacts_size, class: 'form-control gl-form-input'
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded_by_default? ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
= _('Customize CI/CD settings, including Auto DevOps, shared runners, and job artifacts.')
|
||||
= _('Customize CI/CD settings, including Auto DevOps, instance runners, and job artifacts.')
|
||||
= render 'ci_cd'
|
||||
|
||||
= render_if_exists 'admin/application_settings/required_instance_ci_setting', expanded: expanded_by_default?
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@
|
|||
enabled: Gitlab.config.pages.enabled,
|
||||
doc_href: help_instance_configuration_url)
|
||||
|
||||
= feature_entry(_('Shared Runners'),
|
||||
= feature_entry(_('Instance Runners'),
|
||||
href: admin_runners_path,
|
||||
enabled: Gitlab.config.gitlab_ci.shared_runners_enabled)
|
||||
.col-md-4.gl-mb-6
|
||||
|
|
|
|||
|
|
@ -46,5 +46,5 @@
|
|||
= f.fields_for :credit_card_validation do |ff|
|
||||
= ff.gitlab_ui_checkbox_component :credit_card_validated_at,
|
||||
s_('AdminUsers|Validate user account'),
|
||||
help_text: s_('AdminUsers|A user can validate themselves by inputting a credit/debit card, or an admin can manually validate a user. Validated users can use free CI minutes on shared runners.'),
|
||||
help_text: s_('AdminUsers|A user can validate themselves by inputting a credit/debit card, or an admin can manually validate a user. Validated users can use free CI minutes on instance runners.'),
|
||||
checkbox_options: { checked: @user.credit_card_validated_at.present? }
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
- add_page_specific_style 'page_bundles/login'
|
||||
- custom_text = custom_sign_in_description
|
||||
|
||||
!!! 5
|
||||
%html.html-devise-layout{ class: user_application_theme, lang: I18n.locale }
|
||||
= render "layouts/head"
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
- add_page_specific_style 'page_bundles/signup'
|
||||
- add_page_specific_style 'page_bundles/login'
|
||||
!!! 5
|
||||
%html.html-devise-layout{ class: user_application_theme, lang: I18n.locale }
|
||||
= render "layouts/head"
|
||||
%body.signup-page{ class: "#{system_message_class} #{client_class_list}", data: body_data }
|
||||
= header_message
|
||||
= render "layouts/init_client_detection_flags"
|
||||
= render "layouts/header/logo_with_title"
|
||||
.container.gl-align-self-center
|
||||
.content
|
||||
= yield
|
||||
|
||||
= footer_message
|
||||
|
|
@ -43,10 +43,8 @@
|
|||
.add-review-item-modal-trigger{ data: { context_commits_empty: 'true' } }
|
||||
|
||||
- if commits.size == 0 && context_commits.nil?
|
||||
.commits-empty.gl-mt-6
|
||||
.svg-content.svg-150
|
||||
= image_tag('illustrations/empty-state/empty-search-md.svg')
|
||||
%h4
|
||||
= _('Your search didn\'t match any commits.')
|
||||
%p
|
||||
= render Pajamas::EmptyStateComponent.new(svg_path: 'illustrations/empty-state/empty-search-md.svg',
|
||||
title: _('Your search didn\'t match any commits.')) do |c|
|
||||
|
||||
- c.with_description do
|
||||
= _('Try changing or removing filters.')
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
#toggle-shared-runners-form{ data: toggle_shared_runners_settings_data(@project) }
|
||||
|
||||
- if @shared_runners_count == 0
|
||||
= _('This GitLab instance does not provide any shared runners yet. Instance administrators can register shared runners in the admin area.')
|
||||
= _('This GitLab instance does not provide any instance runners yet. Administrators can register instance runners in the admin area.')
|
||||
- else
|
||||
%div{ data: { testid: 'available-shared-runners' } }
|
||||
%h5.gl-mt-6.gl-mb-0
|
||||
= s_('Runners|Available shared runners: %{count}') % {count: @shared_runners_count}
|
||||
= s_('Runners|Available instance runners: %{count}') % {count: @shared_runners_count}
|
||||
%ul.bordered-list
|
||||
= render partial: 'projects/runners/runner', collection: @shared_runners, as: :runner
|
||||
= paginate @shared_runners, theme: "gitlab", param_name: "shared_runners_page", params: { expand_runners: true, anchor: 'js-runners-settings' }
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
%strong
|
||||
= s_('JiraService|Using Jira for issue tracking?')
|
||||
%p.gl-text-center.gl-mb-0
|
||||
- jira_docs_link_url = help_page_url('integration/jira/issues', anchor: 'view-jira-issues')
|
||||
- jira_docs_link_url = help_page_url('integration/jira/configure', anchor: 'view-jira-issues')
|
||||
- jira_docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: jira_docs_link_url }
|
||||
= html_escape(s_('JiraService|%{jira_docs_link_start}Enable the Jira integration%{jira_docs_link_end} to view your Jira issues in GitLab.')) % { jira_docs_link_start: jira_docs_link_start.html_safe, jira_docs_link_end: '</a>'.html_safe }
|
||||
%p.gl-text-center.gl-mb-0.gl-text-gray-500
|
||||
|
|
|
|||
|
|
@ -18,5 +18,5 @@
|
|||
= _('- Not available to run jobs.')
|
||||
|
||||
%p
|
||||
= s_("Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run.")
|
||||
= s_("Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure runners only handle the jobs they are equipped to run.")
|
||||
= link_to _("Learn more."), help_page_path("ci/runners/configure_runners", anchor: "how-the-runner-uses-tags"), target: '_blank'
|
||||
|
|
|
|||
|
|
@ -12,5 +12,5 @@
|
|||
title: s_('Runners|This runner is associated with specific projects.'),
|
||||
dismissible: false) do |c|
|
||||
- c.with_body do
|
||||
= s_('Runners|You can set up a project runner to be used by multiple projects but you cannot make this a shared or group runner.')
|
||||
= s_('Runners|You can set up a project runner to be used by multiple projects but you cannot make this an instance or group runner.')
|
||||
= link_to _('Learn more.'), help_page_path('ci/runners/runners_scope', anchor: 'project-runners'), target: '_blank', rel: 'noopener noreferrer'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
- shared_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('ci/runners/runners_scope', anchor: 'shared-runners') }
|
||||
|
||||
%h4
|
||||
= _('Shared runners')
|
||||
= s_('Runners|Instance runners')
|
||||
|
||||
.bs-callout{ data: { testid: 'shared-runners-description' } }
|
||||
%p= s_('Runners|%{link_start}These runners%{link_end} are available to all groups and projects.').html_safe % { link_start: shared_link_start, link_end: '</a>'.html_safe }
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ module LimitedCapacity
|
|||
|
||||
def register(jid, max_jids)
|
||||
with_redis do |redis|
|
||||
redis.eval(LUA_REGISTER_SCRIPT, keys: [counter_key], argv: [jid.to_s, max_jids.to_i])
|
||||
redis.eval(LUA_REGISTER_SCRIPT, keys: [counter_key], argv: [jid, max_jids])
|
||||
end.present?
|
||||
end
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ module LimitedCapacity
|
|||
end
|
||||
|
||||
def remove_job_keys(redis, keys)
|
||||
redis.srem?(counter_key, keys) if keys.present?
|
||||
redis.srem?(counter_key, keys)
|
||||
end
|
||||
|
||||
def with_redis(&block)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,12 @@ end
|
|||
# :nocov:
|
||||
# rubocop:enable Gitlab/NoCodeCoverageComment
|
||||
|
||||
Redis::Client.prepend(Gitlab::Instrumentation::RedisInterceptor)
|
||||
Redis::Cluster::NodeLoader.prepend(Gitlab::Patch::NodeLoader)
|
||||
Redis::Cluster::SlotLoader.prepend(Gitlab::Patch::SlotLoader)
|
||||
Redis::Cluster::CommandLoader.prepend(Gitlab::Patch::CommandLoader)
|
||||
Redis::Cluster.prepend(Gitlab::Patch::RedisCluster)
|
||||
|
||||
# this only instruments `RedisClient` used in `Sidekiq.redis`
|
||||
RedisClient.register(Gitlab::Instrumentation::RedisClientMiddleware)
|
||||
RedisClient.prepend(Gitlab::Patch::RedisClient)
|
||||
|
|
|
|||
|
|
@ -25,17 +25,7 @@ raise "Do not configure cable.yml with a Redis Cluster as ActionCable only works
|
|||
# https://github.com/rails/rails/blob/bb5ac1623e8de08c1b7b62b1368758f0d3bb6379/actioncable/lib/action_cable/subscription_adapter/redis.rb#L18
|
||||
ActionCable::SubscriptionAdapter::Redis.redis_connector = lambda do |config|
|
||||
args = config.except(:adapter, :channel_prefix)
|
||||
.merge(custom: { instrumentation_class: "ActionCable" })
|
||||
|
||||
if args[:url]
|
||||
# cable.yml uses the url key but unix sockets needs to be passed into Redis
|
||||
# under the `path` key. We do a simple reassignment to resolve that.
|
||||
uri = URI.parse(args[:url])
|
||||
if uri.scheme == 'unix'
|
||||
args[:path] = uri.path
|
||||
args.delete(:url)
|
||||
end
|
||||
end
|
||||
.merge(instrumentation_class: ::Gitlab::Instrumentation::Redis::ActionCable)
|
||||
|
||||
::Redis.new(args)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Peek::Adapters::Redis.prepend ::Gitlab::PerformanceBar::RedisAdapterWhenPeekEnab
|
|||
Peek.singleton_class.prepend ::Gitlab::PerformanceBar::WithTopLevelWarnings
|
||||
|
||||
Rails.application.config.peek.adapter = :redis, {
|
||||
client: Gitlab::Redis::Cache.redis,
|
||||
client: ::Redis.new(Gitlab::Redis::Cache.params),
|
||||
expires_in: 5.minutes
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,12 +29,12 @@ cookie_key = if Rails.env.development?
|
|||
"_gitlab_session"
|
||||
end
|
||||
|
||||
::Redis::Store::Factory.prepend(Gitlab::Patch::RedisStoreFactory)
|
||||
store = Gitlab::Redis::Sessions.store(namespace: Gitlab::Redis::Sessions::SESSION_NAMESPACE)
|
||||
|
||||
Rails.application.configure do
|
||||
config.session_store(
|
||||
:redis_store, # Using the cookie_store would enable session replay attacks.
|
||||
redis_server: Gitlab::Redis::Sessions.params.merge(namespace: Gitlab::Redis::Sessions::SESSION_NAMESPACE),
|
||||
redis_store: store,
|
||||
key: cookie_key,
|
||||
secure: Gitlab.config.gitlab.https,
|
||||
httponly: true,
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ def enable_semi_reliable_fetch_mode?
|
|||
end
|
||||
|
||||
# Custom Queues configuration
|
||||
queues_config_hash = Gitlab::Redis::Queues.params
|
||||
queues_config_hash = Gitlab::Redis::Queues.redis_client_params
|
||||
|
||||
enable_json_logs = Gitlab.config.sidekiq.log_format != 'text'
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddCascadeMathRenderingLimits < Gitlab::Database::Migration[2.2]
|
||||
milestone '16.9'
|
||||
|
||||
enable_lock_retries!
|
||||
|
||||
def change
|
||||
add_column :namespace_settings, :math_rendering_limits_enabled, :boolean, null: true
|
||||
add_column :namespace_settings, :lock_math_rendering_limits_enabled, :boolean, default: false, null: false
|
||||
add_column :application_settings, :lock_math_rendering_limits_enabled, :boolean, default: false, null: false
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DeleteProjectIdComponentIdIndex < Gitlab::Database::Migration[2.2]
|
||||
disable_ddl_transaction!
|
||||
milestone '16.9'
|
||||
INDEX_NAME = 'index_sbom_occurrences_on_project_id_component_id'
|
||||
|
||||
def up
|
||||
remove_concurrent_index_by_name :sbom_occurrences, INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index :sbom_occurrences, [:project_id, :component_id], name: INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
1c76bf19f1eef758de90d3a9750c28e40e8070d9efcd12d63552301a42201e92
|
||||
|
|
@ -0,0 +1 @@
|
|||
bafc4d42dfa19a97208d9967f444d65c5069d44accd48ebf54faf187f981280c
|
||||
|
|
@ -12650,6 +12650,7 @@ CREATE TABLE application_settings (
|
|||
rate_limits jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
elasticsearch_max_code_indexing_concurrency integer DEFAULT 30 NOT NULL,
|
||||
enable_member_promotion_management boolean DEFAULT false NOT NULL,
|
||||
lock_math_rendering_limits_enabled boolean DEFAULT false NOT NULL,
|
||||
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
|
||||
CONSTRAINT app_settings_container_registry_pre_import_tags_rate_positive CHECK ((container_registry_pre_import_tags_rate >= (0)::numeric)),
|
||||
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),
|
||||
|
|
@ -20113,6 +20114,8 @@ CREATE TABLE namespace_settings (
|
|||
lock_toggle_security_policy_custom_ci boolean DEFAULT false NOT NULL,
|
||||
toggle_security_policies_policy_scope boolean,
|
||||
lock_toggle_security_policies_policy_scope boolean DEFAULT false NOT NULL,
|
||||
math_rendering_limits_enabled boolean,
|
||||
lock_math_rendering_limits_enabled boolean DEFAULT false NOT NULL,
|
||||
CONSTRAINT check_0ba93c78c7 CHECK ((char_length(default_branch_name) <= 255)),
|
||||
CONSTRAINT namespace_settings_unique_project_download_limit_alertlist_size CHECK ((cardinality(unique_project_download_limit_alertlist) <= 100)),
|
||||
CONSTRAINT namespace_settings_unique_project_download_limit_allowlist_size CHECK ((cardinality(unique_project_download_limit_allowlist) <= 100))
|
||||
|
|
@ -35494,8 +35497,6 @@ CREATE INDEX index_sbom_occurrences_on_project_id_and_id ON sbom_occurrences USI
|
|||
|
||||
CREATE INDEX index_sbom_occurrences_on_project_id_and_package_manager ON sbom_occurrences USING btree (project_id, package_manager);
|
||||
|
||||
CREATE INDEX index_sbom_occurrences_on_project_id_component_id ON sbom_occurrences USING btree (project_id, component_id);
|
||||
|
||||
CREATE INDEX index_sbom_occurrences_on_source_id ON sbom_occurrences USING btree (source_id);
|
||||
|
||||
CREATE UNIQUE INDEX index_sbom_occurrences_on_uuid ON sbom_occurrences USING btree (uuid);
|
||||
|
|
|
|||
|
|
@ -501,7 +501,8 @@ To sync all groups manually when debugging is unnecessary,
|
|||
[use the Rake task](../../raketasks/ldap.md#run-a-group-sync) instead.
|
||||
|
||||
The output from a manual [group sync](ldap_synchronization.md#group-sync) can show you what happens
|
||||
when GitLab syncs its LDAP group memberships against LDAP.
|
||||
when GitLab syncs its LDAP group memberships against LDAP. Enter the [rails console](#rails-console)
|
||||
and then run:
|
||||
|
||||
```ruby
|
||||
Rails.logger.level = Logger::DEBUG
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ To make sure your configuration is correct:
|
|||
1. Run in the console:
|
||||
|
||||
```ruby
|
||||
redis = Gitlab::Redis::SharedState.redis
|
||||
redis = Redis.new(Gitlab::Redis::SharedState.params)
|
||||
redis.info
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -4548,7 +4548,9 @@ Input type: `GroupUpdateInput`
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationgroupupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationgroupupdatefullpath"></a>`fullPath` | [`ID!`](#id) | Full path of the group that will be updated. |
|
||||
| <a id="mutationgroupupdatesharedrunnerssetting"></a>`sharedRunnersSetting` | [`SharedRunnersSetting!`](#sharedrunnerssetting) | Shared runners availability for the namespace and its descendants. |
|
||||
| <a id="mutationgroupupdatelockmathrenderinglimitsenabled"></a>`lockMathRenderingLimitsEnabled` | [`Boolean`](#boolean) | Indicates if math rendering limits are locked for all descendant groups. |
|
||||
| <a id="mutationgroupupdatemathrenderinglimitsenabled"></a>`mathRenderingLimitsEnabled` | [`Boolean`](#boolean) | Indicates if math rendering limits are used for this group. |
|
||||
| <a id="mutationgroupupdatesharedrunnerssetting"></a>`sharedRunnersSetting` | [`SharedRunnersSetting`](#sharedrunnerssetting) | Shared runners availability for the namespace and its descendants. |
|
||||
|
||||
#### Fields
|
||||
|
||||
|
|
@ -19587,6 +19589,8 @@ GPG signature for a signed commit.
|
|||
| <a id="groupid"></a>`id` | [`ID!`](#id) | ID of the namespace. |
|
||||
| <a id="groupistemporarystorageincreaseenabled"></a>`isTemporaryStorageIncreaseEnabled` **{warning-solid}** | [`Boolean`](#boolean) | **Deprecated** in 16.7. Feature removal, will be completely removed in 17.0. |
|
||||
| <a id="grouplfsenabled"></a>`lfsEnabled` | [`Boolean`](#boolean) | Indicates if Large File Storage (LFS) is enabled for namespace. |
|
||||
| <a id="grouplockmathrenderinglimitsenabled"></a>`lockMathRenderingLimitsEnabled` | [`Boolean`](#boolean) | Indicates if math rendering limits are locked for all descendant groups. |
|
||||
| <a id="groupmathrenderinglimitsenabled"></a>`mathRenderingLimitsEnabled` | [`Boolean`](#boolean) | Indicates if math rendering limits are used for this group. |
|
||||
| <a id="groupmaxaccesslevel"></a>`maxAccessLevel` | [`AccessLevel!`](#accesslevel) | The maximum access level of the current user in the group. |
|
||||
| <a id="groupmentionsdisabled"></a>`mentionsDisabled` | [`Boolean`](#boolean) | Indicates if a group is disabled from getting mentioned. |
|
||||
| <a id="groupname"></a>`name` | [`String!`](#string) | Name of the namespace. |
|
||||
|
|
|
|||
|
|
@ -719,7 +719,9 @@ Example response:
|
|||
]
|
||||
}
|
||||
],
|
||||
"ip_restriction_ranges": null
|
||||
"ip_restriction_ranges": null,
|
||||
"math_rendering_limits_enabled": true,
|
||||
"lock_math_rendering_limits_enabled": false
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -1025,18 +1027,20 @@ PUT /groups/:id
|
|||
| `subgroup_creation_level` | string | no | Allowed to [create subgroups](../user/group/subgroups/index.md#create-a-subgroup). Can be `owner` (Owners), or `maintainer` (users with the Maintainer role). |
|
||||
| `two_factor_grace_period` | integer | no | Time before Two-factor authentication is enforced (in hours). |
|
||||
| `visibility` | string | no | The visibility level of the group. Can be `private`, `internal`, or `public`. |
|
||||
| `extra_shared_runners_minutes_limit` | integer | no | Can be set by administrators only. Additional compute minutes for this group. Self-managed, Premium and Ultimate only. |
|
||||
| `file_template_project_id` | integer | no | The ID of a project to load custom file templates from. Premium and Ultimate only. |
|
||||
| `membership_lock` | boolean | no | Users cannot be added to projects in this group. Premium and Ultimate only. |
|
||||
| `prevent_forking_outside_group` | boolean | no | When enabled, users can **not** fork projects from this group to external namespaces. Premium and Ultimate only. |
|
||||
| `shared_runners_minutes_limit` | integer | no | Can be set by administrators only. Maximum number of monthly compute minutes for this group. Can be `nil` (default; inherit system default), `0` (unlimited), or `> 0`. Self-managed, Premium and Ultimate only. |
|
||||
| `unique_project_download_limit` | integer | no | Maximum number of unique projects a user can download in the specified time period before they are banned. Available only on top-level groups. Default: 0, Maximum: 10,000. Ultimate only. |
|
||||
| `unique_project_download_limit_interval_in_seconds` | integer | no | Time period during which a user can download a maximum amount of projects before they are banned. Available only on top-level groups. Default: 0, Maximum: 864,000 seconds (10 days). Ultimate only. |
|
||||
| `unique_project_download_limit_allowlist` | array of strings | no | List of usernames excluded from the unique project download limit. Available only on top-level groups. Default: `[]`, Maximum: 100 usernames. Ultimate only.|
|
||||
| `unique_project_download_limit_alertlist` | array of integers | no | List of user IDs that are emailed when the unique project download limit is exceeded. Available only on top-level groups. Default: `[]`, Maximum: 100 user IDs. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110201) in GitLab 15.9. Ultimate only.|
|
||||
| `auto_ban_user_on_excessive_projects_download` | boolean | no | When enabled, users are automatically banned from the group when they download more than the maximum number of unique projects specified by `unique_project_download_limit` and `unique_project_download_limit_interval_in_seconds`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94159) in GitLab 15.4. Ultimate only.|
|
||||
| `ip_restriction_ranges` | string | no | Comma-separated list of IP addresses or subnet masks to restrict group access. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/351493) in GitLab 15.4. Premium and Ultimate only.|
|
||||
| `wiki_access_level` | string | no | The wiki access level. Can be `disabled`, `private`, or `enabled`. Premium and Ultimate only.|
|
||||
| `extra_shared_runners_minutes_limit` | integer | no | Can be set by administrators only. Additional compute minutes for this group. Self-managed, Premium and Ultimate only. |
|
||||
| `file_template_project_id` | integer | no | The ID of a project to load custom file templates from. Premium and Ultimate only. |
|
||||
| `membership_lock` | boolean | no | Users cannot be added to projects in this group. Premium and Ultimate only. |
|
||||
| `prevent_forking_outside_group` | boolean | no | When enabled, users can **not** fork projects from this group to external namespaces. Premium and Ultimate only. |
|
||||
| `shared_runners_minutes_limit` | integer | no | Can be set by administrators only. Maximum number of monthly compute minutes for this group. Can be `nil` (default; inherit system default), `0` (unlimited), or `> 0`. Self-managed, Premium and Ultimate only. |
|
||||
| `unique_project_download_limit` | integer | no | Maximum number of unique projects a user can download in the specified time period before they are banned. Available only on top-level groups. Default: 0, Maximum: 10,000. Ultimate only. |
|
||||
| `unique_project_download_limit_interval_in_seconds` | integer | no | Time period during which a user can download a maximum amount of projects before they are banned. Available only on top-level groups. Default: 0, Maximum: 864,000 seconds (10 days). Ultimate only. |
|
||||
| `unique_project_download_limit_allowlist` | array of strings | no | List of usernames excluded from the unique project download limit. Available only on top-level groups. Default: `[]`, Maximum: 100 usernames. Ultimate only.|
|
||||
| `unique_project_download_limit_alertlist` | array of integers | no | List of user IDs that are emailed when the unique project download limit is exceeded. Available only on top-level groups. Default: `[]`, Maximum: 100 user IDs. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110201) in GitLab 15.9. Ultimate only.|
|
||||
| `auto_ban_user_on_excessive_projects_download` | boolean | no | When enabled, users are automatically banned from the group when they download more than the maximum number of unique projects specified by `unique_project_download_limit` and `unique_project_download_limit_interval_in_seconds`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94159) in GitLab 15.4. Ultimate only.|
|
||||
| `ip_restriction_ranges` | string | no | Comma-separated list of IP addresses or subnet masks to restrict group access. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/351493) in GitLab 15.4. Premium and Ultimate only.|
|
||||
| `wiki_access_level` | string | no | The wiki access level. Can be `disabled`, `private`, or `enabled`. Premium and Ultimate only.|
|
||||
| `math_rendering_limits_enabled` | boolean | no | Indicates if math rendering limits are used for this group.|
|
||||
| `lock_math_rendering_limits_enabled` | boolean | no | Indicates if math rendering limits are locked for all descendent groups.|
|
||||
|
||||
NOTE:
|
||||
The `projects` and `shared_projects` attributes in the response are deprecated and [scheduled for removal in API v5](https://gitlab.com/gitlab-org/gitlab/-/issues/213797).
|
||||
|
|
@ -1115,7 +1119,9 @@ Example response:
|
|||
"request_access_enabled": false
|
||||
}
|
||||
],
|
||||
"ip_restriction_ranges": null
|
||||
"ip_restriction_ranges": null,
|
||||
"math_rendering_limits_enabled": true,
|
||||
"lock_math_rendering_limits_enabled": false
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -2494,7 +2494,7 @@ JWTs created this way support OIDC authentication. The required `aud` sub-keywor
|
|||
job_with_id_tokens:
|
||||
id_tokens:
|
||||
ID_TOKEN_1:
|
||||
aud: https://gitlab.com
|
||||
aud: https://vault.example.com
|
||||
ID_TOKEN_2:
|
||||
aud:
|
||||
- https://gcp.com
|
||||
|
|
@ -2502,8 +2502,9 @@ job_with_id_tokens:
|
|||
SIGSTORE_ID_TOKEN:
|
||||
aud: sigstore
|
||||
script:
|
||||
- command_to_authenticate_with_gitlab $ID_TOKEN_1
|
||||
- command_to_authenticate_with_vault $ID_TOKEN_1
|
||||
- command_to_authenticate_with_aws $ID_TOKEN_2
|
||||
- command_to_authenticate_with_gcp $ID_TOKEN_2
|
||||
```
|
||||
|
||||
**Related topics**:
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ GitLab Duo Chat is enabled in the [Staging](https://staging.gitlab.com) and
|
|||
[Staging Ref](https://staging-ref.gitlab.com/) GitLab environments.
|
||||
|
||||
Because GitLab Duo Chat is currently only available to members of groups in the
|
||||
Premium tier, Staging Ref may be an easier place to test changes as a GitLab
|
||||
Premium and Ultimate tiers, Staging Ref may be an easier place to test changes as a GitLab
|
||||
team member because
|
||||
[you can make yourself an instance Admin in Staging Ref](https://handbook.gitlab.com/handbook/engineering/infrastructure/environments/staging-ref/#admin-access)
|
||||
and, as an Admin, easily create licensed groups for testing.
|
||||
|
|
|
|||
|
|
@ -27,9 +27,15 @@ in the relevant projects:
|
|||
|
||||
We also run some documentation tests in the:
|
||||
|
||||
- GitLab CLI project: <https://gitlab.com/gitlab-org/cli/-/blob/main/.gitlab-ci.yml>
|
||||
- GitLab Development Kit project:
|
||||
<https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/.gitlab/ci/test.gitlab-ci.yml>.
|
||||
- Gitaly project: <https://gitlab.com/gitlab-org/gitaly/-/blob/master/.gitlab-ci.yml>.
|
||||
- GitLab Duo Plugin for JetBrains: <https://gitlab.com/gitlab-org/editor-extensions/gitlab-jetbrains-plugin/-/blob/main/.gitlab-ci.yml>
|
||||
- GitLab VS Code Extension project: <https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/blob/main/.gitlab-ci.yml>.
|
||||
- GitLab Plugin for Neovim project: <https://gitlab.com/gitlab-org/editor-extensions/gitlab.vim/-/blob/main/.gitlab-ci.yml>.
|
||||
- GitLab Language Server project: <https://gitlab.com/gitlab-org/editor-extensions/gitlab-lsp/-/blob/main/.gitlab-ci.yml>.
|
||||
- GitLab Extension for Visual Studio project: <https://gitlab.com/gitlab-org/editor-extensions/gitlab-visual-studio-extension/-/blob/main/.gitlab-ci.yml>.
|
||||
|
||||
## Run tests locally
|
||||
|
||||
|
|
|
|||
|
|
@ -60,18 +60,10 @@ On rows where `json.event` is `Failed Attempt`, you can find valuable debugging
|
|||
|
||||
#### View Telesign SMS status update logs
|
||||
|
||||
To view logs of Telesign status updates for an SMS sent to a user:
|
||||
|
||||
1. Get a `telesign_reference_id` value for an SMS sent to a specific user:
|
||||
To view Telesign status updates logs for SMS sent to a user, query the GitLab production logs with:
|
||||
|
||||
```plaintext
|
||||
json.message: "IdentityVerification::Phone" AND json.username:<username>`
|
||||
```
|
||||
|
||||
1. Search for status update logs associated with `telesign_reference_id` value:
|
||||
|
||||
```plaintext
|
||||
json.message: "IdentityVerification::Phone" AND json.event: "Telesign transaction status update" AND json.telesign_reference_id:replace_ref_id_value_here`
|
||||
json.message: "IdentityVerification::Phone" AND json.event: "Telesign transaction status update" AND json.username:<username>`
|
||||
```
|
||||
|
||||
Status update logs include the following fields:
|
||||
|
|
|
|||
|
|
@ -81,10 +81,10 @@ Developers are highly encouraged to use [hash-tags](https://redis.io/docs/refere
|
|||
where appropriate to facilitate future adoption of Redis Cluster in more Redis types. For example, the Namespace model uses hash-tags
|
||||
for its [config cache keys](https://gitlab.com/gitlab-org/gitlab/-/blob/1a12337058f260d38405886d82da5e8bb5d8da0b/app/models/namespace.rb#L786).
|
||||
|
||||
To perform multi-key commands, developers may use the [`.pipelined`](https://github.com/redis-rb/redis-cluster-client#interfaces) method which splits and sends commands to each node and aggregates replies.
|
||||
To perform multi-key commands, developers may use the [`Gitlab::Redis::CrossSlot::Pipeline`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/redis/cross_slot.rb) wrapper.
|
||||
However, this does not work for [transactions](https://redis.io/docs/interact/transactions/) as Redis Cluster does not support cross-slot transactions.
|
||||
|
||||
For `Rails.cache`, we handle the `MGET` command found in `read_multi_get` by [patching it](https://gitlab.com/gitlab-org/gitlab/-/blob/c2bad2aac25e2f2778897bd4759506a72b118b15/lib/gitlab/patch/redis_cache_store.rb#L10) to use the `.pipelined` method.
|
||||
For `Rails.cache`, we handle the `MGET` command found in `read_multi_get` by [patching it](https://gitlab.com/gitlab-org/gitlab/-/blob/c2bad2aac25e2f2778897bd4759506a72b118b15/lib/gitlab/patch/redis_cache_store.rb#L10) to use the `Gitlab::Redis::CrossSlot::Pipeline` wrapper.
|
||||
The minimum size of the pipeline is set to 1000 commands and it can be adjusted by using the `GITLAB_REDIS_CLUSTER_PIPELINE_BATCH_LIMIT` environment variable.
|
||||
|
||||
## Redis in structured logging
|
||||
|
|
|
|||
|
|
@ -147,8 +147,8 @@ module Gitlab
|
|||
# Don't use multistore if redis.foo configuration is not provided
|
||||
return super if config_fallback?
|
||||
|
||||
primary_store = init_redis(params)
|
||||
secondary_store = init_redis(config_fallback.params)
|
||||
primary_store = ::Redis.new(params)
|
||||
secondary_store = ::Redis.new(config_fallback.params)
|
||||
|
||||
MultiStore.new(primary_store, secondary_store, store_name)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ Prerequisites:
|
|||
- You must have a [Jira Cloud API token](#create-a-jira-cloud-api-token) and the email address you used to create the token.
|
||||
- If you've enabled
|
||||
[IP allowlists](https://support.atlassian.com/security-and-access-policies/docs/specify-ip-addresses-for-product-access/), add the
|
||||
[GitLab.com IP range](../../user/gitlab_com/index.md#ip-range) to the allowlist to [view Jira issues](issues.md#view-jira-issues) in GitLab.
|
||||
[GitLab.com IP range](../../user/gitlab_com/index.md#ip-range) to the allowlist to [view Jira issues](#view-jira-issues) in GitLab.
|
||||
- **For Jira Data Center or Jira Server**, you must have one of the following:
|
||||
- [Jira username and password](jira_server_configuration.md).
|
||||
- Jira personal access token (GitLab 16.0 and later).
|
||||
|
|
@ -67,7 +67,7 @@ To configure your project settings in GitLab:
|
|||
- For **Jira issue regex**, [enter a regex pattern](issues.md#define-a-regex-pattern).
|
||||
- For **Jira issue prefix**, [enter a prefix](issues.md#define-a-prefix).
|
||||
1. Optional. In the **Issues** section:
|
||||
- To [view issues](issues.md#view-jira-issues) from a single Jira project in a GitLab project:
|
||||
- To [view issues](#view-jira-issues) from a single Jira project in a GitLab project:
|
||||
1. Select the **Enable Jira issues** checkbox.
|
||||
1. Enter the Jira project key.
|
||||
|
||||
|
|
@ -86,6 +86,36 @@ To configure your project settings in GitLab:
|
|||
|
||||
Your GitLab project can now interact with all Jira projects on your instance.
|
||||
|
||||
## View Jira issues
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Premium, Ultimate
|
||||
**Offering:** SaaS, self-managed
|
||||
|
||||
> - Ability to enable Jira issues at the group level [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/325715) in GitLab 16.9.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Ensure the Jira issue integration is [configured](#configure-the-integration)
|
||||
and the **Enable Jira issues** checkbox is selected.
|
||||
|
||||
To view issues from a single Jira project in a GitLab project:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Plan > Jira issues**.
|
||||
|
||||
By default, the issues are sorted by **Created date**.
|
||||
The most recently created issues appear at the top.
|
||||
You can [search and filter the issue list](issues.md#search-and-filter-the-issue-list)
|
||||
and select an issue to view that issue in GitLab.
|
||||
|
||||
Issues are grouped into the following tabs based on their
|
||||
[Jira status](https://confluence.atlassian.com/adminjiraserver070/defining-status-field-values-749382903.html):
|
||||
|
||||
- **Open**: issues with any Jira status other than **Done**.
|
||||
- **Closed**: issues with a **Done** Jira status.
|
||||
- **All**: issues with any Jira status.
|
||||
|
||||
## Create a Jira issue for a vulnerability
|
||||
|
||||
DETAILS:
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ This table shows the features available with the Jira issue integration and the
|
|||
| Mention a Jira issue ID in a GitLab branch name, and the Jira issue shows the branch name. | **{dotted-circle}** No | **{check-circle}** Yes, in the Jira issue's development panel. |
|
||||
| Add time tracking to a Jira issue. | **{dotted-circle}** No | **{check-circle}** Yes, with Jira Smart Commits. |
|
||||
| Use a GitLab commit or merge request to transition a Jira issue. |**{check-circle}** Yes, only a single transition. Typically used to close the Jira issue. | **{check-circle}** Yes, transition the Jira issue to any state with Jira Smart Commits. |
|
||||
| [View a list of Jira issues](issues.md#view-jira-issues). | **{check-circle}** Yes | **{dotted-circle}** No |
|
||||
| [View a list of Jira issues](configure.md#view-jira-issues). | **{check-circle}** Yes | **{dotted-circle}** No |
|
||||
| [Create a Jira issue for a vulnerability](configure.md#create-a-jira-issue-for-a-vulnerability). | **{check-circle}** Yes | **{dotted-circle}** No |
|
||||
| Create a GitLab branch from a Jira issue. | **{dotted-circle}** No | **{check-circle}** Yes, in the Jira issue's development panel. |
|
||||
| Mention a Jira issue ID in a GitLab merge request, branch name, or any of the last 5,000 commits to the branch after the last successful deployment to the environment to sync a GitLab deployment to a Jira issue. | **{dotted-circle}** No | **{check-circle}** Yes, in the Jira issue's development panel. |
|
||||
|
|
|
|||
|
|
@ -146,34 +146,7 @@ Consider this example:
|
|||
- GitLab adds a formatted comment to Jira, linking back to the commit that
|
||||
resolved the issue. You can [disable comments](#disable-comments-on-jira-issues).
|
||||
|
||||
## View Jira issues
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Premium, Ultimate
|
||||
**Offering:** SaaS, self-managed
|
||||
|
||||
You can view and search issues from a selected Jira project directly in GitLab,
|
||||
provided your GitLab administrator [has configured the integration](configure.md#configure-the-integration).
|
||||
|
||||
To view Jira issues:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Plan > Jira issues**.
|
||||
|
||||
The issues are sorted by **Created date** by default, with the most recently created issues listed at the top.
|
||||
|
||||
- To display the most recently updated issues first, select **Updated date**.
|
||||
- You can [search and filter the issue list](#search-and-filter-the-issue-list).
|
||||
- You can [select an issue from the list to view the issue in GitLab](https://gitlab.com/gitlab-org/gitlab/-/issues/299832).
|
||||
|
||||
Issues are grouped into tabs based on their
|
||||
[Jira status](https://confluence.atlassian.com/adminjiraserver070/defining-status-field-values-749382903.html):
|
||||
|
||||
- **Open** tab: All issues with a Jira status in any category other than Done.
|
||||
- **Closed** tab: All issues with a Jira status categorized as Done.
|
||||
- **All** tab: All issues of any status.
|
||||
|
||||
### Search and filter the issue list
|
||||
## Search and filter the issue list
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Premium, Ultimate
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ end
|
|||
|
||||
## Jira issue list
|
||||
|
||||
When [viewing Jira issues](issues.md#view-jira-issues) in GitLab, you might encounter the following issues.
|
||||
When [viewing Jira issues](configure.md#view-jira-issues) in GitLab, you might encounter the following issues.
|
||||
|
||||
### `500 We're sorry` when accessing a Jira issue in GitLab
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ Some features are still in development. View details about [support for each sta
|
|||
| Goal | Feature | Tier/Offering/Status |
|
||||
|---|---|---|
|
||||
| Helps you write code more efficiently by showing code suggestions as you type. <br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=hCAyCTacdAQ) | [Code Suggestions](project/repository/code_suggestions/index.md) | **Free Open Access** subject to the [Testing Agreement](https://handbook.gitlab.com/handbook/legal/testing-agreement/) available through February 14, 2024:<br>- SaaS, All subscription tiers <br>- Self-managed, Premium and Ultimate tiers<br><br> **Generally Available** starting February 15, 2024: <br> - SaaS, self-managed <br>- Premium and Ultimate tiers as part of [GitLab Duo Pro](https://about.gitlab.com/press/releases/2024-01-17-gitlab-announces-pricing-of-gitLab-duo-pro.html/) |
|
||||
| Processes and generates text and code in a conversational manner. Helps you quickly identify useful information in large volumes of text in issues, epics, code, and GitLab documentation. | [Chat](gitlab_duo_chat.md) | **Beta Access** subject to the [Testing Agreement](https://handbook.gitlab.com/handbook/legal/testing-agreement/):<br>- SaaS, self-managed <br>- Ultimate tier<br><br>**Status:** Beta |
|
||||
| Processes and generates text and code in a conversational manner. Helps you quickly identify useful information in large volumes of text in issues, epics, code, and GitLab documentation. | [Chat](gitlab_duo_chat.md) | **Beta Access** subject to the [Testing Agreement](https://handbook.gitlab.com/handbook/legal/testing-agreement/):<br>- SaaS, self-managed <br>- Premium and Ultimate tiers<br><br>**Status:** Beta |
|
||||
| Helps you discover or recall Git commands when and where you need them. | [Git suggestions](../editor_extensions/gitlab_cli/index.md#gitlab-duo-commands) | **Tier:** Ultimate <br>**Offering:** SaaS <br>**Status:** Experiment |
|
||||
| Assists with quickly getting everyone up to speed on lengthy conversations to help ensure you are all on the same page. | [Discussion summary](#summarize-issue-discussions-with-discussion-summary) | **Tier:** Ultimate <br>**Offering:** SaaS <br>**Status:** Experiment |
|
||||
| Generates issue descriptions. | [Issue description generation](#summarize-an-issue-with-issue-description-generation) | **Tier:** Ultimate<br>**Offering:** SaaS <br>**Status:** Experiment |
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ To track DORA metrics in these cases, you can [create a deployment record](../..
|
|||
### Measure DORA metrics with Jira
|
||||
|
||||
- Deployment frequency and lead time for changes are calculated based on GitLab CI/CD and Merge Requests (MRs), and do not require Jira data.
|
||||
- Time to restore service and change failure rate require [GitLab incidents](../../operations/incident_management/manage_incidents.md) for the calculation. For more information, see [Measure DORA Time to restore service and Change failure rate with external incidents](#measure-dora-time-to-restore-service-and-change-failure-rate-with-external-incidents).
|
||||
- Time to restore service and change failure rate require [GitLab incidents](../../operations/incident_management/manage_incidents.md) for the calculation. For more information, see [Measure DORA Time to restore service and Change failure rate with external incidents](#measure-dora-time-to-restore-service-and-change-failure-rate-with-external-incidents) and the [Jira incident replicator guide](https://gitlab.com/smathur/jira-incident-replicator).
|
||||
|
||||
### Measure DORA Time to restore service and Change failure rate with external incidents
|
||||
|
||||
|
|
|
|||
|
|
@ -7,13 +7,14 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
# GitLab Duo Chat
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Ultimate
|
||||
**Tier:** Premium, Ultimate
|
||||
**Offering:** SaaS, self-managed
|
||||
**Status:** Beta
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117695) as an [Experiment](../policy/experiment-beta-support.md#experiment) for SaaS in GitLab 16.0.
|
||||
> - Changed to [Beta](../policy/experiment-beta-support.md#beta) for SaaS in GitLab 16.6.
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/11251) as a [Beta](../policy/experiment-beta-support.md#beta) for self-managed in GitLab 16.8.
|
||||
> - Changed from Ultimate to [Premium](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142808) tier in GitLab 16.9.
|
||||
|
||||
GitLab Duo Chat is your personal AI-powered assistant for boosting productivity.
|
||||
It can assist various tasks of your daily work with the AI-generated content.
|
||||
|
|
|
|||
|
|
@ -102,14 +102,10 @@ and tags. For example, if the template contains a protected branch:
|
|||
- In the project created from the template, the branch allows _you_ to merge into
|
||||
the default branch.
|
||||
|
||||
<!-- ## Troubleshooting
|
||||
## Troubleshooting
|
||||
|
||||
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
|
||||
one might have when setting this up, or when something is changed, or on upgrading, it's
|
||||
important to describe those, too. Think of things that may go wrong and include them here.
|
||||
This is important to minimize requests for support, and to avoid doc comments with
|
||||
questions that you know someone might ask.
|
||||
### Administrator cannot see custom group-level project templates when creating a project
|
||||
|
||||
Each scenario can be a third-level heading, for example `### Getting error message X`.
|
||||
If you have none to add when creating a doc, leave this section in place
|
||||
but commented out to help encourage others to add to it in the future. -->
|
||||
Custom group-level project templates are only available to group members.
|
||||
If the administrator account you are using is not a member of a group,
|
||||
you can't access the templates.
|
||||
|
|
|
|||
|
|
@ -450,7 +450,7 @@ for the ability to set merge request approval rules for groups is tracked in
|
|||
## Enable Experiment and Beta features
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Ultimate, Premium
|
||||
**Tier:** Premium, Ultimate
|
||||
**Offering:** SaaS
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118222) in GitLab 16.0.
|
||||
|
|
|
|||
|
|
@ -71,7 +71,10 @@ To view the organizations you have access to:
|
|||
|
||||
1. On the left sidebar, select **Organizations** (**{organization}**) and find the organization you want to manage.
|
||||
1. Select **Manage > Groups and projects**.
|
||||
1. To switch between groups and projects, use the **Display** filter next to the search box.
|
||||
1. Optional. Filter the results:
|
||||
- To search for specific groups or projects, in the search box enter your search term.
|
||||
- To view only groups or projects, from the **Display** dropdown list select an option.
|
||||
1. Optional. To sort the results by name, date created, or date updated, from the dropdown list select an option. Then select ascending (**{sort-lowest}**) or descending (**{sort-highest}**) order.
|
||||
|
||||
## Manage users
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ Watch a [video demo](https://youtu.be/yvLxtkvsFDA) of how to publish npm package
|
|||
|
||||
### Authentication to the package registry
|
||||
|
||||
You need an token to publish a package. There are different tokens available depending on what you're trying to achieve. For more information, review the [guidance on tokens](../../../user/packages/package_registry/index.md#authenticate-with-the-registry).
|
||||
You need a token to publish a package. There are different tokens available depending on what you're trying to achieve. For more information, review the [guidance on tokens](../../../user/packages/package_registry/index.md#authenticate-with-the-registry).
|
||||
|
||||
- If your organization uses two factor authentication (2FA), you must use a personal access token with the scope set to `api`.
|
||||
- If you are publishing a package via CI/CD pipelines, you must use a CI job token.
|
||||
|
|
|
|||
|
|
@ -182,9 +182,9 @@ For more information, [see the forking workflow documentation](../repository/for
|
|||
|
||||
### Set the default target project
|
||||
|
||||
By default, merge requests originating from a fork target your fork, not the upstream project.
|
||||
If you frequently contribute back to the upstream project, and want to target it
|
||||
by default, change the default target for your fork.
|
||||
By default, merge requests originating from a fork target the upstream project, not the forked project.
|
||||
|
||||
You can configure your forked project to be the default target rather than the upstream project.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ module API
|
|||
expose :emails_enabled, documentation: { type: 'boolean' }
|
||||
expose :mentions_disabled
|
||||
expose :lfs_enabled?, as: :lfs_enabled
|
||||
expose :math_rendering_limits_enabled, documentation: { type: 'boolean' }
|
||||
expose :lock_math_rendering_limits_enabled, documentation: { type: 'boolean' }
|
||||
expose :default_branch_protection
|
||||
expose :default_branch_protection_defaults
|
||||
expose :avatar_url do |group, options|
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ module API
|
|||
|
||||
params :optional_update_params do
|
||||
optional :prevent_sharing_groups_outside_hierarchy, type: Boolean, desc: 'Prevent sharing groups within this namespace with any groups outside the namespace. Only available on top-level groups.'
|
||||
optional :lock_math_rendering_limits_enabled, type: Boolean, desc: 'Indicates if math rendering limits are locked for all descendent groups.'
|
||||
optional :math_rendering_limits_enabled, type: Boolean, desc: 'Indicates if math rendering limits are used for this group.'
|
||||
end
|
||||
|
||||
params :optional_update_params_ee do
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ module Banzai
|
|||
class BroadcastMessagePipeline < DescriptionPipeline
|
||||
def self.filters
|
||||
@filters ||= FilterArray[
|
||||
Filter::BlockquoteFenceFilter,
|
||||
Filter::MarkdownFilter,
|
||||
Filter::BroadcastMessageSanitizationFilter,
|
||||
Filter::EmojiFilter,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ module Banzai
|
|||
FilterArray[
|
||||
Filter::MarkdownPreEscapeFilter,
|
||||
Filter::DollarMathPreFilter,
|
||||
Filter::BlockquoteFenceFilter,
|
||||
Filter::MarkdownFilter,
|
||||
Filter::DollarMathPostFilter,
|
||||
Filter::MarkdownPostEscapeFilter
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ module Banzai
|
|||
Filter::NormalizeSourceFilter,
|
||||
Filter::TruncateSourceFilter,
|
||||
Filter::FrontMatterFilter,
|
||||
Filter::BlockquoteFenceFilter,
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ module Gitlab
|
|||
|
||||
Gitlab::Redis::SharedState.with do |redis|
|
||||
redis.multi do |r|
|
||||
r.zadd(key, time, ip) if ip
|
||||
r.zadd(key, time, ip)
|
||||
r.zremrangebyscore(key, 0, time - config.unique_ips_limit_time_window)
|
||||
r.zcard(key)
|
||||
end.last
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ module Gitlab
|
|||
ttl_jitter = 2.hours.to_i
|
||||
|
||||
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
|
||||
redis.pipelined do |pipeline|
|
||||
Gitlab::Redis::CrossSlot::Pipeline.new(redis).pipelined do |pipeline|
|
||||
keys.each { |key| pipeline.expire(key, ttl_duration + rand(-ttl_jitter..ttl_jitter)) }
|
||||
end
|
||||
end
|
||||
|
|
@ -25,7 +25,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def redis
|
||||
@redis ||= Gitlab::Redis::Cache.redis
|
||||
@redis ||= ::Redis.new(Gitlab::Redis::Cache.params)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ module Gitlab
|
|||
def store_in_cache
|
||||
with_redis do |redis|
|
||||
redis.pipelined do |p|
|
||||
p.mapped_hmset(cache_key, { sha: sha.to_s, status: status.to_s, ref: ref.to_s })
|
||||
p.mapped_hmset(cache_key, { sha: sha, status: status, ref: ref })
|
||||
p.expire(cache_key, STATUS_KEY_TTL)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ module Gitlab
|
|||
key = cache_key_for(raw_key)
|
||||
|
||||
with_redis do |redis|
|
||||
redis.sismember(key, value || value.to_s)
|
||||
redis.sismember(key, value)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -162,7 +162,7 @@ module Gitlab
|
|||
def self.write_multiple(mapping, key_prefix: nil, timeout: TIMEOUT)
|
||||
with_redis do |redis|
|
||||
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
|
||||
redis.pipelined do |pipeline|
|
||||
Gitlab::Redis::CrossSlot::Pipeline.new(redis).pipelined do |pipeline|
|
||||
mapping.each do |raw_key, value|
|
||||
key = cache_key_for("#{key_prefix}#{raw_key}")
|
||||
|
||||
|
|
|
|||
|
|
@ -197,9 +197,7 @@ module Gitlab
|
|||
record_hit_ratio(results)
|
||||
|
||||
results.map! do |result|
|
||||
unless result.nil?
|
||||
Gitlab::Json.parse(gzip_decompress(result.force_encoding(Encoding::UTF_8)), symbolize_names: true)
|
||||
end
|
||||
Gitlab::Json.parse(gzip_decompress(result), symbolize_names: true) unless result.nil?
|
||||
end
|
||||
|
||||
file_paths.zip(results).to_h
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ module Gitlab
|
|||
def write_multiple(mapping)
|
||||
with_redis do |redis|
|
||||
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
|
||||
redis.pipelined do |pipelined|
|
||||
Gitlab::Redis::CrossSlot::Pipeline.new(redis).pipelined do |pipelined|
|
||||
mapping.each do |raw_key, value|
|
||||
key = cache_key_for(raw_key)
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ module Gitlab
|
|||
with_redis do |redis|
|
||||
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
|
||||
if Gitlab::Redis::ClusterUtil.cluster?(redis)
|
||||
redis.pipelined do |pipeline|
|
||||
Gitlab::Redis::CrossSlot::Pipeline.new(redis).pipelined do |pipeline|
|
||||
keys.each { |key| pipeline.get(key) }
|
||||
end
|
||||
else
|
||||
|
|
@ -54,7 +54,7 @@ module Gitlab
|
|||
content.map! do |lines|
|
||||
next unless lines
|
||||
|
||||
Gitlab::Json.parse(gzip_decompress(lines.force_encoding(Encoding::UTF_8))).map! do |line|
|
||||
Gitlab::Json.parse(gzip_decompress(lines)).map! do |line|
|
||||
Gitlab::Diff::Line.safe_init_from_hash(line)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ module Gitlab
|
|||
|
||||
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
|
||||
with_redis do |redis|
|
||||
redis.pipelined do |pipeline|
|
||||
Gitlab::Redis::CrossSlot::Pipeline.new(redis).pipelined do |pipeline|
|
||||
keys.each_with_index do |key, i|
|
||||
pipeline.set(redis_shared_state_key(key), etags[i], ex: EXPIRY_TIME, nx: only_if_missing)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -60,7 +60,6 @@ module Gitlab
|
|||
|
||||
def self.cancel(key, uuid)
|
||||
return unless key.present?
|
||||
return unless uuid.present?
|
||||
|
||||
Gitlab::Redis::SharedState.with do |redis|
|
||||
redis.eval(LUA_CANCEL_SCRIPT, keys: [ensure_prefixed_key(key)], argv: [uuid])
|
||||
|
|
@ -111,7 +110,7 @@ module Gitlab
|
|||
# false if the lease is taken by a different UUID or inexistent.
|
||||
def renew
|
||||
Gitlab::Redis::SharedState.with do |redis|
|
||||
result = redis.eval(LUA_RENEW_SCRIPT, keys: [@redis_shared_state_key], argv: [@uuid, @timeout.to_i])
|
||||
result = redis.eval(LUA_RENEW_SCRIPT, keys: [@redis_shared_state_key], argv: [@uuid, @timeout])
|
||||
result == @uuid
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails'
|
||||
require 'redis-clustering'
|
||||
require 'redis'
|
||||
|
||||
module Gitlab
|
||||
module Instrumentation
|
||||
|
|
@ -230,7 +230,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def key_slot(key)
|
||||
::RedisClient::Cluster::KeySlotConverter.convert(extract_hash_tag(key))
|
||||
::Redis::Cluster::KeySlotConverter.convert(extract_hash_tag(key))
|
||||
end
|
||||
|
||||
# This is almost identical to Redis::Cluster::Command#extract_hash_tag,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Instrumentation
|
||||
module RedisInterceptor
|
||||
include RedisHelper
|
||||
|
||||
def call(command)
|
||||
instrument_call([command], instrumentation_class) do
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def call_pipeline(pipeline)
|
||||
instrument_call(pipeline.commands, instrumentation_class, true) do
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def write(command)
|
||||
measure_write_size(command, instrumentation_class) if ::RequestStore.active?
|
||||
super
|
||||
end
|
||||
|
||||
def read
|
||||
result = super
|
||||
measure_read_size(result, instrumentation_class) if ::RequestStore.active?
|
||||
result
|
||||
end
|
||||
|
||||
def ensure_connected
|
||||
super do
|
||||
instrument_reconnection_errors do
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def instrument_reconnection_errors
|
||||
yield
|
||||
rescue ::Redis::BaseConnectionError => ex
|
||||
instrumentation_class.instance_count_connection_exception(ex)
|
||||
|
||||
raise ex
|
||||
end
|
||||
|
||||
# That's required so it knows which GitLab Redis instance
|
||||
# it's interacting with in order to categorize accordingly.
|
||||
#
|
||||
def instrumentation_class
|
||||
@options[:instrumentation_class] # rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -100,7 +100,7 @@ module Gitlab
|
|||
def refresh_keys_expiration
|
||||
with_redis do |redis|
|
||||
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
|
||||
redis.pipelined do |pipeline|
|
||||
Gitlab::Redis::CrossSlot::Pipeline.new(redis).pipelined do |pipeline|
|
||||
pipeline.expire(issue_ids_key, REDIS_EXPIRY_TIME)
|
||||
pipeline.expire(current_index_key, REDIS_EXPIRY_TIME)
|
||||
pipeline.expire(current_project_key, REDIS_EXPIRY_TIME)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ module Gitlab
|
|||
|
||||
data = Gitlab::Redis::Cache.with do |r|
|
||||
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
|
||||
r.pipelined do |pipeline|
|
||||
Gitlab::Redis::CrossSlot::Pipeline.new(r).pipelined do |pipeline|
|
||||
subjects.each do |subject|
|
||||
new(subject).read(pipeline)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Patch
|
||||
module CommandLoader
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
class_methods do
|
||||
# Shuffle the node list to spread out initial connection creation amongst all nodes
|
||||
#
|
||||
# The input is a Redis::Cluster::Node instance which is an Enumerable.
|
||||
# `super` receives an Array of Redis::Client instead of a Redis::Cluster::Node
|
||||
def load(nodes)
|
||||
super(nodes.to_a.shuffle)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Patch to address https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/2212#note_1287996694
|
||||
# It uses hostname instead of IP address if the former is present in `CLUSTER NODES` output.
|
||||
if Gem::Version.new(Redis::VERSION) > Gem::Version.new('4.8.1')
|
||||
raise 'New version of redis detected, please remove or update this patch'
|
||||
end
|
||||
|
||||
module Gitlab
|
||||
module Patch
|
||||
module NodeLoader
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
class_methods do
|
||||
# Shuffle the node list to spread out initial connection creation amongst all nodes
|
||||
#
|
||||
# The input is a Redis::Cluster::Node instance which is an Enumerable.
|
||||
# `super` receives an Array of Redis::Client instead of a Redis::Cluster::Node
|
||||
def load_flags(nodes)
|
||||
super(nodes.to_a.shuffle)
|
||||
end
|
||||
end
|
||||
|
||||
def self.prepended(base)
|
||||
base.class_eval do
|
||||
# monkey-patches https://github.com/redis/redis-rb/blob/v4.8.0/lib/redis/cluster/node_loader.rb#L23
|
||||
def self.fetch_node_info(node)
|
||||
node.call(%i[cluster nodes]).split("\n").map(&:split).to_h do |arr|
|
||||
[
|
||||
extract_host_identifier(arr[1]),
|
||||
(arr[2].split(',') & %w[master slave]).first # rubocop:disable Naming/InclusiveLanguage
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
# Since `CLUSTER SLOT` uses the preferred endpoint determined by
|
||||
# the `cluster-preferred-endpoint-type` config value, we will prefer hostname over IP address.
|
||||
# See https://redis.io/commands/cluster-nodes/ for details on the output format.
|
||||
#
|
||||
# @param [String] Address info matching fhe format: <ip:port@cport[,hostname[,auxiliary_field=value]*]>
|
||||
def self.extract_host_identifier(node_address)
|
||||
ip_chunk, hostname, _auxiliaries = node_address.split(',')
|
||||
return ip_chunk.split('@').first if hostname.blank?
|
||||
|
||||
port = ip_chunk.split('@').first.split(':')[1]
|
||||
"#{hostname}:#{port}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -20,7 +20,7 @@ module Gitlab
|
|||
delete_count = 0
|
||||
redis.with do |conn|
|
||||
entries.each_slice(pipeline_batch_size) do |subset|
|
||||
delete_count += conn.pipelined do |pipeline|
|
||||
delete_count += Gitlab::Redis::CrossSlot::Pipeline.new(conn).pipelined do |pipeline|
|
||||
subset.each { |entry| pipeline.del(entry) }
|
||||
end.sum
|
||||
end
|
||||
|
|
@ -58,7 +58,7 @@ module Gitlab
|
|||
|
||||
def pipeline_mget(conn, keys)
|
||||
keys.each_slice(pipeline_batch_size).flat_map do |subset|
|
||||
conn.pipelined do |p|
|
||||
Gitlab::Redis::CrossSlot::Pipeline.new(conn).pipelined do |p|
|
||||
subset.each { |key| p.get(key) }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Patch to expose `find_node_key` method for cross-slot pipelining
|
||||
# In redis v5.0.x, cross-slot pipelining is implemented via redis-cluster-client.
|
||||
# This patch should be removed since there is no need for it.
|
||||
# Gitlab::Redis::CrossSlot and its usage should be removed as well.
|
||||
if Gem::Version.new(Redis::VERSION) != Gem::Version.new('4.8.0')
|
||||
raise 'New version of redis detected, please remove or update this patch'
|
||||
end
|
||||
|
||||
module Gitlab
|
||||
module Patch
|
||||
module RedisCluster
|
||||
# _find_node_key exposes a private function of the same name in Redis::Cluster.
|
||||
# See https://github.com/redis/redis-rb/blob/v4.8.0/lib/redis/cluster.rb#L282
|
||||
def _find_node_key(command)
|
||||
find_node_key(command)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Patch
|
||||
module RedisStoreFactory
|
||||
def create
|
||||
# rubocop:disable Gitlab/ModuleWithInstanceVariables -- patched code references @options in redis-store
|
||||
opt = @options
|
||||
# rubocop:enable Gitlab/ModuleWithInstanceVariables
|
||||
return Gitlab::Redis::ClusterStore.new(opt) if opt[:nodes]
|
||||
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Patch
|
||||
module SlotLoader
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
class_methods do
|
||||
# Shuffle the node list to spread out initial connection creation amongst all nodes
|
||||
#
|
||||
# The input is a Redis::Cluster::Node instance which is an Enumerable.
|
||||
# `super` receives an Array of Redis::Client instead of a Redis::Cluster::Node
|
||||
def load(nodes)
|
||||
super(nodes.to_a.shuffle)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'redis-clustering'
|
||||
require 'redis/store/ttl'
|
||||
require 'redis/store/interface'
|
||||
require 'redis/store/namespace'
|
||||
require 'redis/store/serialization'
|
||||
|
||||
module Gitlab
|
||||
module Redis
|
||||
class ClusterStore < ::Redis::Cluster
|
||||
include ::Redis::Store::Interface
|
||||
|
||||
def initialize(options = {})
|
||||
orig_options = options.dup
|
||||
|
||||
@serializer = orig_options.key?(:serializer) ? orig_options.delete(:serializer) : Marshal
|
||||
|
||||
unless orig_options[:marshalling].nil?
|
||||
# `marshalling` only used here, might not be supported in `super`
|
||||
@serializer = orig_options.delete(:marshalling) ? Marshal : nil
|
||||
end
|
||||
|
||||
_remove_unsupported_options(options)
|
||||
super(options)
|
||||
|
||||
_extend_marshalling
|
||||
_extend_namespace orig_options
|
||||
end
|
||||
|
||||
# copies ::Redis::Store::Ttl implementation in a redis-v5 compatible manner
|
||||
def set(key, value, options = nil)
|
||||
ttl = get_ttl(options)
|
||||
if ttl
|
||||
setex(key, ttl.to_i, value, raw: true)
|
||||
else
|
||||
super(key, value)
|
||||
end
|
||||
end
|
||||
|
||||
# copies ::Redis::Store::Ttl implementation in a redis-v5 compatible manner
|
||||
def setnx(key, value, options = nil)
|
||||
ttl = get_ttl(options)
|
||||
if ttl
|
||||
multi do |m|
|
||||
m.setnx(key, value)
|
||||
m.expire(key, ttl)
|
||||
end
|
||||
else
|
||||
super(key, value)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_ttl(options)
|
||||
# https://github.com/redis-store/redis-store/blob/v1.10.0/lib/redis/store/ttl.rb#L37
|
||||
options[:expire_after] || options[:expires_in] || options[:expire_in] if options
|
||||
end
|
||||
|
||||
def _remove_unsupported_options(options)
|
||||
# Unsupported keywords should be removed to avoid errors
|
||||
# https://github.com/redis-rb/redis-client/blob/v0.13.0/lib/redis_client/config.rb#L21
|
||||
options.delete(:raw)
|
||||
options.delete(:serializer)
|
||||
options.delete(:marshalling)
|
||||
options.delete(:namespace)
|
||||
options.delete(:scheme)
|
||||
end
|
||||
|
||||
def _extend_marshalling
|
||||
extend ::Redis::Store::Serialization unless @serializer.nil?
|
||||
end
|
||||
|
||||
def _extend_namespace(options)
|
||||
@namespace = options[:namespace]
|
||||
extend ::Redis::Store::Namespace
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -13,14 +13,14 @@ module Gitlab
|
|||
if obj.is_a?(MultiStore)
|
||||
cluster?(obj.primary_store) || cluster?(obj.secondary_store)
|
||||
else
|
||||
obj.is_a?(::Redis::Cluster)
|
||||
obj.respond_to?(:_client) && obj._client.is_a?(::Redis::Cluster)
|
||||
end
|
||||
end
|
||||
|
||||
def batch_unlink(keys, redis)
|
||||
expired_count = 0
|
||||
keys.each_slice(1000) do |subset|
|
||||
expired_count += redis.pipelined do |pipeline|
|
||||
expired_count += Gitlab::Redis::CrossSlot::Pipeline.new(redis).pipelined do |pipeline|
|
||||
subset.each { |key| pipeline.unlink(key) }
|
||||
end.sum
|
||||
end
|
||||
|
|
@ -30,7 +30,7 @@ module Gitlab
|
|||
# Redis cluster alternative to mget
|
||||
def batch_get(keys, redis)
|
||||
keys.each_slice(1000).flat_map do |subset|
|
||||
redis.pipelined do |pipeline|
|
||||
Gitlab::Redis::CrossSlot::Pipeline.new(redis).pipelined do |pipeline|
|
||||
subset.map { |key| pipeline.get(key) }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,53 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Redis
|
||||
module CommandBuilder
|
||||
extend self
|
||||
|
||||
# Ref: https://github.com/redis-rb/redis-client/blob/v0.19.1/lib/redis_client/command_builder.rb
|
||||
# we modify the command builder to convert nil to strings as this behaviour was present in
|
||||
# https://github.com/redis/redis-rb/blob/v4.8.0/lib/redis/connection/command_helper.rb#L20
|
||||
#
|
||||
# Note that we only adopt the Ruby3.x-compatible logic in .generate.
|
||||
# Symbol.method_defined?(:name) is true in Ruby 3
|
||||
def generate(args, kwargs = nil)
|
||||
command = args.flat_map do |element|
|
||||
case element
|
||||
when Hash
|
||||
element.flatten
|
||||
else
|
||||
element
|
||||
end
|
||||
end
|
||||
|
||||
kwargs&.each do |key, value|
|
||||
if value
|
||||
if value == true
|
||||
command << key.name
|
||||
else
|
||||
command << key.name << value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
command.map! do |element|
|
||||
case element
|
||||
when String
|
||||
element
|
||||
when Symbol
|
||||
element.name
|
||||
when Integer, Float, NilClass
|
||||
element.to_s
|
||||
else
|
||||
raise TypeError, "Unsupported command argument type: #{element.class}"
|
||||
end
|
||||
end
|
||||
|
||||
raise ArgumentError, "can't issue an empty redis command" if command.empty?
|
||||
|
||||
command
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Redis
|
||||
module CrossSlot
|
||||
class Router
|
||||
attr_reader :node_mapping, :futures, :node_sequence, :cmd_queue
|
||||
|
||||
delegate :respond_to_missing?, to: :@redis
|
||||
|
||||
# This map contains redis-rb methods which does not map directly
|
||||
# to a standard Redis command. It is used transform unsupported commands to standard commands
|
||||
# to find the node key for unsupported commands.
|
||||
#
|
||||
# Redis::Cluster::Command only contains details of commands which the Redis Server
|
||||
# returns. Hence, commands like mapped_hmget and hscan_each internally will call the
|
||||
# base command, hmget and hscan respectively.
|
||||
#
|
||||
# See https://github.com/redis/redis-rb/blob/v4.8.0/lib/redis/cluster/command.rb
|
||||
UNSUPPORTED_CMD_MAPPING = {
|
||||
# Internally, redis-rb calls the supported Redis command and transforms the output.
|
||||
# See https://github.com/redis/redis-rb/blob/v4.8.0/lib/redis/commands/hashes.rb#L104
|
||||
mapped_hmget: :hmget
|
||||
}.freeze
|
||||
|
||||
# Initializes the CrossSlot::Router
|
||||
# @param {::Redis}
|
||||
def initialize(redis)
|
||||
@redis = redis
|
||||
@node_mapping = {}
|
||||
@futures = {}
|
||||
@node_sequence = []
|
||||
@cmd_queue = []
|
||||
end
|
||||
|
||||
# For now we intercept every redis.call and return a Gitlab-Future object.
|
||||
# This method groups every commands to a node for fan-out. Commands are grouped using the first key.
|
||||
#
|
||||
# rubocop:disable Style/MissingRespondToMissing
|
||||
def method_missing(cmd, *args, **kwargs, &blk)
|
||||
# Note that we can re-map the command without affecting execution as it is
|
||||
# solely for finding the node key. The original cmd will be executed.
|
||||
node = @redis._client._find_node_key([UNSUPPORTED_CMD_MAPPING.fetch(cmd, cmd)] + args)
|
||||
|
||||
@node_mapping[node] ||= []
|
||||
@futures[node] ||= []
|
||||
|
||||
@node_sequence << node
|
||||
@node_mapping[node] << [cmd, args, kwargs || {}, blk]
|
||||
f = Future.new
|
||||
@futures[node] << f
|
||||
@cmd_queue << [f, cmd, args, kwargs || {}, blk]
|
||||
f
|
||||
end
|
||||
# rubocop:enable Style/MissingRespondToMissing
|
||||
end
|
||||
|
||||
# Wraps over redis-rb's Future in
|
||||
# https://github.com/redis/redis-rb/blob/v4.8.0/lib/redis/pipeline.rb#L244
|
||||
class Future
|
||||
def set(future, is_val = false)
|
||||
@redis_future = future
|
||||
@is_val = is_val
|
||||
end
|
||||
|
||||
def value
|
||||
return @redis_val if @is_val
|
||||
|
||||
@redis_future.value
|
||||
end
|
||||
end
|
||||
|
||||
# Pipeline allows cross-slot pipelined to be called. The fan-out logic is implemented in
|
||||
# https://github.com/redis-rb/redis-cluster-client/blob/master/lib/redis_client/cluster/pipeline.rb
|
||||
# which is available in redis-rb v5.0.
|
||||
#
|
||||
# This file can be deprecated after redis-rb v4.8.0 is upgraded to v5.0
|
||||
class Pipeline
|
||||
# Initializes the CrossSlot::Pipeline
|
||||
# @param {::Redis}
|
||||
def initialize(redis)
|
||||
@redis = redis
|
||||
end
|
||||
|
||||
# pipelined is used in place of ::Redis `.pipelined` when running in a cluster context
|
||||
# where cross-slot operations may happen.
|
||||
def pipelined(&block)
|
||||
# Directly call .pipelined and defer the pipeline execution to MultiStore.
|
||||
# MultiStore could wrap over 0, 1, or 2 Redis Cluster clients, handling it here
|
||||
# will not work for 2 clients since the key-slot topology can differ.
|
||||
if use_cross_slot_pipelining?
|
||||
router = Router.new(@redis)
|
||||
yield router
|
||||
execute_commands(router)
|
||||
else
|
||||
# use redis-rb's pipelined method
|
||||
@redis.pipelined(&block)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def use_cross_slot_pipelining?
|
||||
!@redis.instance_of?(::Gitlab::Redis::MultiStore) && @redis._client.instance_of?(::Redis::Cluster)
|
||||
end
|
||||
|
||||
def execute_commands(router)
|
||||
router.node_mapping.each do |node_key, commands|
|
||||
# TODO possibly use Threads to speed up but for now `n` is 3-5 which is small.
|
||||
@redis.pipelined do |p|
|
||||
commands.each_with_index do |command, idx|
|
||||
future = router.futures[node_key][idx]
|
||||
cmd, args, kwargs, blk = command
|
||||
future.set(p.public_send(cmd, *args, **kwargs, &blk)) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
router.node_sequence.map do |node_key|
|
||||
router.futures[node_key].shift.value
|
||||
end
|
||||
rescue ::Redis::CommandError => err
|
||||
if err.message.start_with?('MOVED', 'ASK')
|
||||
Gitlab::ErrorTracking.log_exception(err)
|
||||
return execute_commands_sequentially(router)
|
||||
end
|
||||
|
||||
raise
|
||||
end
|
||||
|
||||
def execute_commands_sequentially(router)
|
||||
router.cmd_queue.map do |command|
|
||||
future, cmd, args, kwargs, blk = command
|
||||
future.set(@redis.public_send(cmd, *args, **kwargs, &blk), true) # rubocop:disable GitlabSecurity/PublicSend
|
||||
future.value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -432,6 +432,16 @@ module Gitlab
|
|||
|
||||
# rubocop:disable GitlabSecurity/PublicSend
|
||||
def send_command(redis_instance, command_name, *args, **kwargs, &block)
|
||||
# Run wrapped pipeline for each instance individually so that the fan-out is distinct.
|
||||
# If both primary and secondary are Redis Clusters, the slot-node distribution could
|
||||
# be different.
|
||||
#
|
||||
# We ignore args and kwargs since `pipelined` does not accept arguments
|
||||
# See https://github.com/redis/redis-rb/blob/v4.8.0/lib/redis.rb#L164
|
||||
if command_name.to_s == 'pipelined' && redis_instance._client.instance_of?(::Redis::Cluster)
|
||||
return Gitlab::Redis::CrossSlot::Pipeline.new(redis_instance).pipelined(&block)
|
||||
end
|
||||
|
||||
if block
|
||||
# Make sure that block is wrapped and executed only on the redis instance that is executing the block
|
||||
redis_instance.send(command_name, *args, **kwargs) do |*params|
|
||||
|
|
@ -452,7 +462,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def redis_store?(pool)
|
||||
pool.with { |c| c.instance_of?(Gitlab::Redis::MultiStore) || c.is_a?(::Redis) || c.is_a?(::Redis::Cluster) }
|
||||
pool.with { |c| c.instance_of?(Gitlab::Redis::MultiStore) || c.is_a?(::Redis) }
|
||||
end
|
||||
|
||||
def validate_stores!
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue