Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-11-16 18:10:52 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-11-16 18:10:52 +0300
commit0552020767452da44de2bf5424096f2cb2ea6bf5 (patch)
tree9579d9f0ad3c730c33883130ec23420e80d1c5dc
parente3748b81ca29b24197276767e245158d8f84fda3 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.eslintrc.yml1
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/review-apps/main.gitlab-ci.yml6
-rw-r--r--.rubocop_todo/layout/line_length.yml1
-rw-r--r--.rubocop_todo/style/next.yml28
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_admin_variables.vue1
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue59
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_variable_settings.vue6
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_variable_shared.vue6
-rw-r--r--app/assets/javascripts/content_editor/content_editor.stories.js1
-rw-r--r--app/assets/javascripts/vue_shared/components/code_block.stories.js1
-rw-r--r--app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.stories.js1
-rw-r--r--app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.stories.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/pagination_bar/pagination_bar.stories.js1
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.stories.js1
-rw-r--r--app/assets/javascripts/vue_shared/components/user_deletion_obstacles/user_deletion_obstacles_list.stories.js2
-rw-r--r--app/assets/stylesheets/framework/variables.scss54
-rw-r--r--app/assets/stylesheets/startup/startup-dark.scss260
-rw-r--r--app/assets/stylesheets/startup/startup-general.scss124
-rw-r--r--app/assets/stylesheets/startup/startup-signin.scss94
-rw-r--r--app/assets/stylesheets/themes/_dark.scss24
-rw-r--r--app/controllers/jira_connect/application_controller.rb1
-rw-r--r--app/graphql/types/metadata_type.rb2
-rw-r--r--app/models/active_session.rb38
-rw-r--r--app/models/awareness_session.rb49
-rw-r--r--app/models/incident_management/timeline_event_tag.rb2
-rw-r--r--app/models/instance_metadata.rb5
-rw-r--r--app/services/incident_management/timeline_events/create_service.rb34
-rw-r--r--app/validators/nested_attributes_duplicates_validator.rb10
-rw-r--r--app/views/devise/confirmations/almost_there.haml2
-rw-r--r--config/feature_flags/development/board_grouped_by_epic_performance.yml8
-rw-r--r--config/open_api.yml2
-rw-r--r--config/routes.rb1
-rw-r--r--doc/api/graphql/reference/index.md1
-rw-r--r--doc/api/metadata.md4
-rw-r--r--doc/api/suggestions.md2
-rw-r--r--doc/development/pipelines/index.md11
-rw-r--r--doc/user/product_analytics/index.md48
-rw-r--r--lib/api/api.rb2
-rw-r--r--lib/api/entities/metadata.rb1
-rw-r--r--lib/api/entities/metrics/dashboard/annotation.rb14
-rw-r--r--lib/api/metadata.rb1
-rw-r--r--lib/api/metrics/dashboard/annotations.rb25
-rw-r--r--lib/backup/manager.rb14
-rw-r--r--lib/banzai/filter/external_link_filter.rb12
-rw-r--r--lib/banzai/filter/footnote_filter.rb28
-rw-r--r--lib/banzai/filter/kroki_filter.rb16
-rw-r--r--lib/banzai/filter/math_filter.rb25
-rw-r--r--lib/banzai/filter/plantuml_filter.rb10
-rw-r--r--lib/banzai/filter/table_of_contents_filter.rb16
-rw-r--r--lib/gitlab/background_migration/encrypt_static_object_token.rb8
-rw-r--r--lib/gitlab/cache/import/caching.rb12
-rw-r--r--lib/gitlab/database.rb12
-rw-r--r--lib/gitlab/discussions_diff/highlight_cache.rb10
-rw-r--r--lib/gitlab/etag_caching/store.rb10
-rw-r--r--lib/gitlab/gitaly_client/blob_service.rb17
-rw-r--r--lib/gitlab/gitaly_client/cleanup_service.rb6
-rw-r--r--lib/gitlab/gitaly_client/conflicts_service.rb7
-rw-r--r--lib/gitlab/gitaly_client/object_pool_service.rb20
-rw-r--r--lib/gitlab/gitaly_client/operation_service.rb31
-rw-r--r--lib/gitlab/gitaly_client/praefect_info_service.rb6
-rw-r--r--lib/gitlab/gitaly_client/ref_service.rb41
-rw-r--r--lib/gitlab/gitaly_client/remote_service.rb5
-rw-r--r--lib/gitlab/gitaly_client/repository_service.rb81
-rw-r--r--lib/gitlab/gitaly_client/with_feature_flag_actors.rb74
-rw-r--r--lib/gitlab/import_export/attributes_permitter.rb8
-rw-r--r--lib/gitlab/import_export/base/relation_object_saver.rb8
-rw-r--r--lib/gitlab/instrumentation/redis_base.rb4
-rw-r--r--lib/gitlab/instrumentation/redis_cluster_validator.rb223
-rw-r--r--lib/gitlab/instrumentation/redis_interceptor.rb3
-rw-r--r--lib/gitlab/issues/rebalancing/state.rb26
-rw-r--r--lib/gitlab/manifest_import/metadata.rb8
-rw-r--r--lib/gitlab/markdown_cache/redis/store.rb8
-rw-r--r--lib/gitlab/metrics/samplers/base_sampler.rb8
-rw-r--r--lib/gitlab/pagination/keyset/in_operator_optimization/strategies/record_loader_strategy.rb10
-rw-r--r--lib/gitlab/reference_extractor.rb8
-rw-r--r--lib/tasks/gitlab/assets.rake22
-rw-r--r--lib/tasks/gitlab/db/validate_config.rake18
-rw-r--r--locale/gitlab.pot13
-rw-r--r--package.json4
-rwxr-xr-xscripts/perf/query_limiting_report.rb26
-rwxr-xr-xscripts/qa/quarantine-types-check18
-rwxr-xr-xscripts/review_apps/automated_cleanup.rb6
-rw-r--r--scripts/review_apps/base-config.yaml45
-rwxr-xr-xscripts/review_apps/review-apps.sh28
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/metadata.json34
-rw-r--r--spec/frontend/access_tokens/components/__snapshots__/expires_at_field_spec.js.snap1
-rw-r--r--spec/frontend/ci_variable_list/components/ci_admin_variables_spec.js1
-rw-r--r--spec/frontend/ci_variable_list/components/ci_group_variables_spec.js1
-rw-r--r--spec/frontend/ci_variable_list/components/ci_project_variables_spec.js1
-rw-r--r--spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js90
-rw-r--r--spec/frontend/ci_variable_list/components/ci_variable_settings_spec.js2
-rw-r--r--spec/frontend/ci_variable_list/components/ci_variable_shared_spec.js2
-rw-r--r--spec/graphql/mutations/incident_management/timeline_event/create_spec.rb88
-rw-r--r--spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb65
-rw-r--r--spec/lib/gitlab/import_export/import_test_coverage_spec.rb10
-rw-r--r--spec/lib/gitlab/instrumentation/redis_cluster_validator_spec.rb29
-rw-r--r--spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb19
-rw-r--r--spec/lib/gitlab/redis/multi_store_spec.rb30
-rw-r--r--spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb1
-rw-r--r--spec/models/incident_management/timeline_event_tag_spec.rb4
-rw-r--r--spec/models/instance_metadata_spec.rb3
-rw-r--r--spec/presenters/packages/npm/package_presenter_spec.rb8
-rw-r--r--spec/requests/api/graphql/metadata_query_spec.rb3
-rw-r--r--spec/requests/jira_connect/cors_preflight_checks_controller_spec.rb7
-rw-r--r--spec/requests/jira_connect/subscriptions_controller_spec.rb36
-rw-r--r--spec/services/incident_management/timeline_events/create_service_spec.rb42
-rw-r--r--spec/tooling/lib/tooling/helm3_client_spec.rb26
-rw-r--r--spec/views/devise/confirmations/almost_there.html.haml_spec.rb37
-rw-r--r--tooling/lib/tooling/helm3_client.rb2
-rw-r--r--yarn.lock20
112 files changed, 1541 insertions, 916 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml
index 659ed2a0010..f814bdc6434 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -163,6 +163,7 @@ overrides:
- '*.stories.js'
rules:
filenames/match-regex: off
+ '@gitlab/require-i18n-strings': off
- files:
- '*.graphql'
plugins:
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 936b9410a60..edbbe90a774 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -52,7 +52,7 @@ workflow:
CREATE_INCIDENT_FOR_PIPELINE_FAILURE: "true"
NOTIFY_PIPELINE_FAILURE_CHANNEL: "master-broken"
# Run pipelines for ruby3 branch
- - if: '$CI_COMMIT_BRANCH == "ruby3"'
+ - if: '$CI_COMMIT_BRANCH == "ruby3" && $CI_PIPELINE_SOURCE == "schedule"'
variables:
RUBY_VERSION: "3.0"
NOTIFY_PIPELINE_FAILURE_CHANNEL: "f_ruby3"
diff --git a/.gitlab/ci/review-apps/main.gitlab-ci.yml b/.gitlab/ci/review-apps/main.gitlab-ci.yml
index 0c3fd847c99..7ecc8db3b1b 100644
--- a/.gitlab/ci/review-apps/main.gitlab-ci.yml
+++ b/.gitlab/ci/review-apps/main.gitlab-ci.yml
@@ -118,7 +118,7 @@ review-deploy:
- run_timed_command "download_chart"
- run_timed_command "deploy" || (display_deployment_debug && exit 1)
- run_timed_command "verify_deploy"|| (display_deployment_debug && exit 1)
- - run_timed_command "disable_sign_ups" || (delete_release && exit 1)
+ - run_timed_command "disable_sign_ups"
after_script:
# Run seed-dast-test-data.sh only when DAST_RUN is set to true. This is to pupulate review app with data for DAST scan.
# Set DAST_RUN to true when jobs are manually scheduled.
@@ -166,7 +166,7 @@ review-delete-deployment:
- .review:rules:review-delete-deployment
stage: prepare
script:
- - delete_release
+ - delete_helm_release
review-stop:
extends:
@@ -176,4 +176,4 @@ review-stop:
stage: deploy
needs: []
script:
- - delete_namespace
+ - delete_helm_release
diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml
index 75e04ddcbd4..b5fc199c0ca 100644
--- a/.rubocop_todo/layout/line_length.yml
+++ b/.rubocop_todo/layout/line_length.yml
@@ -2983,7 +2983,6 @@ Layout/LineLength:
- 'lib/api/members.rb'
- 'lib/api/merge_request_diffs.rb'
- 'lib/api/merge_requests.rb'
- - 'lib/api/metrics/dashboard/annotations.rb'
- 'lib/api/metrics/user_starred_dashboards.rb'
- 'lib/api/milestone_responses.rb'
- 'lib/api/namespaces.rb'
diff --git a/.rubocop_todo/style/next.yml b/.rubocop_todo/style/next.yml
index 9570bd7b036..295aa2f6878 100644
--- a/.rubocop_todo/style/next.yml
+++ b/.rubocop_todo/style/next.yml
@@ -2,32 +2,4 @@
# Cop supports --auto-correct.
Style/Next:
Exclude:
- - 'app/models/concerns/integrations/slack_mattermost_notifier.rb'
- - 'app/validators/nested_attributes_duplicates_validator.rb'
- - 'ee/app/services/security/ingestion/tasks/update_vulnerability_uuids.rb'
- - 'ee/db/fixtures/development/20_vulnerabilities.rb'
- - 'ee/lib/ee/audit/protected_branches_changes_auditor.rb'
- - 'ee/lib/gitlab/elastic/search_results.rb'
- - 'ee/lib/system_check/geo/authorized_keys_check.rb'
- - 'lib/backup/manager.rb'
- - 'lib/banzai/filter/external_link_filter.rb'
- - 'lib/banzai/filter/footnote_filter.rb'
- - 'lib/banzai/filter/kroki_filter.rb'
- - 'lib/banzai/filter/math_filter.rb'
- - 'lib/banzai/filter/plantuml_filter.rb'
- - 'lib/banzai/filter/table_of_contents_filter.rb'
- - 'lib/gitlab/background_migration/encrypt_static_object_token.rb'
- - 'lib/gitlab/database.rb'
- 'lib/gitlab/fogbugz_import/importer.rb'
- - 'lib/gitlab/gitaly_client/repository_service.rb'
- - 'lib/gitlab/import_export/attributes_permitter.rb'
- - 'lib/gitlab/import_export/base/relation_object_saver.rb'
- - 'lib/gitlab/metrics/samplers/base_sampler.rb'
- - 'lib/gitlab/pagination/keyset/in_operator_optimization/strategies/record_loader_strategy.rb'
- - 'lib/gitlab/reference_extractor.rb'
- - 'lib/tasks/gitlab/assets.rake'
- - 'lib/tasks/gitlab/db/validate_config.rake'
- - 'scripts/perf/query_limiting_report.rb'
- - 'scripts/qa/quarantine-types-check'
- - 'spec/lib/gitlab/import_export/import_test_coverage_spec.rb'
- - 'spec/presenters/packages/npm/package_presenter_spec.rb'
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_admin_variables.vue b/app/assets/javascripts/ci_variable_list/components/ci_admin_variables.vue
index afdac28cbd6..719696f682e 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_admin_variables.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_admin_variables.vue
@@ -28,6 +28,7 @@ export default {
<ci-variable-shared
:are-scoped-variables-available="false"
component-name="InstanceVariables"
+ :hide-environment-scope="true"
:mutation-data="$options.mutationData"
:refetch-after-mutation="true"
:query-data="$options.queryData"
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue
index 346a038000e..94f8cb9e906 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue
@@ -86,6 +86,11 @@ export default {
required: false,
default: () => [],
},
+ hideEnvironmentScope: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
mode: {
type: String,
required: true,
@@ -310,33 +315,35 @@ export default {
/>
</gl-form-group>
- <gl-form-group
- label-for="ci-variable-env"
- class="gl-w-half"
- data-testid="environment-scope"
- >
- <template #label>
- {{ __('Environment scope') }}
- <gl-link
- :title="$options.environmentScopeLinkTitle"
- :href="environmentScopeLink"
- target="_blank"
- data-testid="environment-scope-link"
- >
- <gl-icon name="question" :size="12" />
- </gl-link>
- </template>
- <ci-environments-dropdown
- v-if="areScopedVariablesAvailable"
- class="gl-w-full"
- :selected-environment-scope="variable.environmentScope"
- :environments="joinedEnvironments"
- @select-environment="setEnvironmentScope"
- @create-environment-scope="createEnvironmentScope"
- />
+ <template v-if="!hideEnvironmentScope">
+ <gl-form-group
+ label-for="ci-variable-env"
+ class="gl-w-half"
+ data-testid="environment-scope"
+ >
+ <template #label>
+ {{ __('Environment scope') }}
+ <gl-link
+ :title="$options.environmentScopeLinkTitle"
+ :href="environmentScopeLink"
+ target="_blank"
+ data-testid="environment-scope-link"
+ >
+ <gl-icon name="question" :size="12" />
+ </gl-link>
+ </template>
+ <ci-environments-dropdown
+ v-if="areScopedVariablesAvailable"
+ class="gl-w-full"
+ :selected-environment-scope="variable.environmentScope"
+ :environments="joinedEnvironments"
+ @select-environment="setEnvironmentScope"
+ @create-environment-scope="createEnvironmentScope"
+ />
- <gl-form-input v-else :value="$options.defaultScope" class="gl-w-full" readonly />
- </gl-form-group>
+ <gl-form-input v-else :value="$options.defaultScope" class="gl-w-full" readonly />
+ </gl-form-group>
+ </template>
</div>
<gl-form-group :label="__('Flags')" label-for="ci-variable-flags">
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_settings.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_settings.vue
index 81e3a983ea3..94fd6c3892c 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_variable_settings.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_settings.vue
@@ -19,6 +19,11 @@ export default {
required: false,
default: () => [],
},
+ hideEnvironmentScope: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
isLoading: {
type: Boolean,
required: false,
@@ -78,6 +83,7 @@ export default {
v-if="showModal"
:are-scoped-variables-available="areScopedVariablesAvailable"
:environments="environments"
+ :hide-environment-scope="hideEnvironmentScope"
:variables="variables"
:mode="mode"
:selected-variable="selectedVariable"
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_shared.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_shared.vue
index 48081fe28f1..7ee250cea98 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_variable_shared.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_shared.vue
@@ -31,6 +31,11 @@ export default {
type: String,
default: null,
},
+ hideEnvironmentScope: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
id: {
required: false,
type: String,
@@ -216,6 +221,7 @@ export default {
<template>
<ci-variable-settings
:are-scoped-variables-available="areScopedVariablesAvailable"
+ :hide-environment-scope="hideEnvironmentScope"
:is-loading="isLoading"
:variables="ciVariables"
:environments="environments"
diff --git a/app/assets/javascripts/content_editor/content_editor.stories.js b/app/assets/javascripts/content_editor/content_editor.stories.js
index 2d4226ccd33..9e1a4bfe361 100644
--- a/app/assets/javascripts/content_editor/content_editor.stories.js
+++ b/app/assets/javascripts/content_editor/content_editor.stories.js
@@ -11,7 +11,6 @@ const Template = (_, { argTypes }) => ({
template: '<content-editor v-bind="$props" @initialized="loadContent" />',
methods: {
loadContent(contentEditor) {
- // eslint-disable-next-line @gitlab/require-i18n-strings
contentEditor.setSerializedContent('Hello content editor');
},
},
diff --git a/app/assets/javascripts/vue_shared/components/code_block.stories.js b/app/assets/javascripts/vue_shared/components/code_block.stories.js
index e02a346c1de..994913dc1a8 100644
--- a/app/assets/javascripts/vue_shared/components/code_block.stories.js
+++ b/app/assets/javascripts/vue_shared/components/code_block.stories.js
@@ -13,6 +13,5 @@ const Template = (args, { argTypes }) => ({
export const Default = Template.bind({});
Default.args = {
- // eslint-disable-next-line @gitlab/require-i18n-strings
code: `git commit -a "Message"\ngit push`,
};
diff --git a/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.stories.js b/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.stories.js
index 7ecc309db52..b56434f746e 100644
--- a/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.stories.js
+++ b/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.stories.js
@@ -1,4 +1,3 @@
-/* eslint-disable @gitlab/require-i18n-strings */
import ConfirmDanger from './confirm_danger.vue';
export default {
diff --git a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.stories.js b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.stories.js
index 8256d953466..a48b8bcfa8e 100644
--- a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.stories.js
+++ b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.stories.js
@@ -1,5 +1,3 @@
-/* eslint-disable @gitlab/require-i18n-strings */
-
import { __ } from '~/locale';
import DropdownWidget from './dropdown_widget.vue';
diff --git a/app/assets/javascripts/vue_shared/components/pagination_bar/pagination_bar.stories.js b/app/assets/javascripts/vue_shared/components/pagination_bar/pagination_bar.stories.js
index f16afc77164..fd9d69bae22 100644
--- a/app/assets/javascripts/vue_shared/components/pagination_bar/pagination_bar.stories.js
+++ b/app/assets/javascripts/vue_shared/components/pagination_bar/pagination_bar.stories.js
@@ -1,4 +1,3 @@
-/* eslint-disable @gitlab/require-i18n-strings */
import PaginationBar from './pagination_bar.vue';
export default {
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js
index 8a2bab4cb9a..465ee9aa0d4 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js
+++ b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js
@@ -1,5 +1,3 @@
-/* eslint-disable @gitlab/require-i18n-strings */
-
import TodoButton from './todo_button.vue';
export default {
diff --git a/app/assets/javascripts/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.stories.js b/app/assets/javascripts/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.stories.js
index e621442e601..84615386fe2 100644
--- a/app/assets/javascripts/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.stories.js
+++ b/app/assets/javascripts/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.stories.js
@@ -1,4 +1,3 @@
-/* eslint-disable @gitlab/require-i18n-strings */
import TooltipOnTruncate from './tooltip_on_truncate.vue';
const defaultWidth = '250px';
diff --git a/app/assets/javascripts/vue_shared/components/user_deletion_obstacles/user_deletion_obstacles_list.stories.js b/app/assets/javascripts/vue_shared/components/user_deletion_obstacles/user_deletion_obstacles_list.stories.js
index 1f0f4cde234..0815fdd9aac 100644
--- a/app/assets/javascripts/vue_shared/components/user_deletion_obstacles/user_deletion_obstacles_list.stories.js
+++ b/app/assets/javascripts/vue_shared/components/user_deletion_obstacles/user_deletion_obstacles_list.stories.js
@@ -1,5 +1,3 @@
-/* eslint-disable @gitlab/require-i18n-strings */
-
import { OBSTACLE_TYPES } from './constants';
import UserDeletionObstaclesList from './user_deletion_obstacles_list.vue';
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 504fae466bb..99284ea0a64 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -88,14 +88,6 @@ $white-normal: #f0f0f0 !default;
$white-dark: #eaeaea !default;
$white-transparent: rgba($white, 0.8) !default;
-$gray-lightest: #fdfdfd !default;
-$gray-light: #fafafa !default;
-$gray-lighter: #f9f9f9 !default;
-$gray-normal: #f5f5f5 !default;
-$gray-dark: darken($gray-light, $darken-dark-factor) !default;
-$gray-darker: #eee !default;
-$gray-darkest: #c4c4c4 !default;
-
$purple: #6d49cb !default;
$purple-light: #ede8fb !default;
@@ -103,11 +95,6 @@ $black: #000 !default;
$black-transparent: rgba(0, 0, 0, 0.3) !default;
$almost-black: #242424 !default;
-$t-gray-a-02: rgba($black, 0.02) !default;
-$t-gray-a-04: rgba($black, 0.04) !default;
-$t-gray-a-06: rgba($black, 0.06) !default;
-$t-gray-a-08: rgba($black, 0.08) !default;
-
$green-50: #ecf4ee !default;
$green-100: #c3e6cd !default;
$green-200: #91d4a8 !default;
@@ -168,18 +155,33 @@ $purple-800: #453894 !default;
$purple-900: #2f2a6b !default;
$purple-950: #232150 !default;
-$gray-10: #f5f5f5 !default;
-$gray-50: #f0f0f0 !default;
-$gray-100: #dbdbdb !default;
-$gray-200: #bfbfbf !default;
-$gray-300: #999 !default;
-$gray-400: #868686 !default;
-$gray-500: #666 !default;
-$gray-600: #5e5e5e !default;
-$gray-700: #525252 !default;
-$gray-800: #404040 !default;
-$gray-900: #303030 !default;
-$gray-950: #1f1f1f !default;
+$gray-10: #fbfafd !default;
+$gray-50: #ececef !default;
+$gray-100: #dcdcde !default;
+$gray-200: #bfbfc3 !default;
+$gray-300: #a4a3a8 !default;
+$gray-400: #89888d !default;
+$gray-500: #737278 !default;
+$gray-600: #626168 !default;
+$gray-700: #535158 !default;
+$gray-800: #434248 !default;
+$gray-900: #333238 !default;
+$gray-950: #1f1e24 !default;
+
+$gray-lightest: lighten($gray-10, 1) !default;
+$gray-light: $gray-10 !default;
+$gray-lighter: lighten($gray-50, 4) !default;
+$gray-normal: lighten($gray-50, 2) !default;
+$gray-dark: darken($gray-light, $darken-dark-factor) !default;
+$gray-darker: $gray-50 !default;
+$gray-darkest: $gray-200 !default;
+
+$t-gray-a-02: rgba($gray-950, 0.02) !default;
+$t-gray-a-04: rgba($gray-950, 0.04) !default;
+$t-gray-a-06: rgba($gray-950, 0.06) !default;
+$t-gray-a-08: rgba($gray-950, 0.08) !default;
+$t-gray-a-16: rgba($gray-950, 0.16) !default;
+$t-gray-a-24: rgba($gray-950, 0.24) !default;
$greens: (
'50': $green-50,
@@ -370,7 +372,7 @@ $border-gray-normal-dashed: darken($gray-normal, $darken-border-dashed-factor);
/*
* UI elements
*/
-$contextual-sidebar-bg-color: #f5f5f5;
+$contextual-sidebar-bg-color: $gray-10;
$contextual-sidebar-border-color: #e9e9e9;
$border-color: $gray-100;
$shadow-color: $t-gray-a-08;
diff --git a/app/assets/stylesheets/startup/startup-dark.scss b/app/assets/stylesheets/startup/startup-dark.scss
index 3a81eec34e3..11131cc1a4b 100644
--- a/app/assets/stylesheets/startup/startup-dark.scss
+++ b/app/assets/stylesheets/startup/startup-dark.scss
@@ -6,15 +6,15 @@
color-scheme: dark;
}
body.gl-dark {
- --gray-10: #1f1f1f;
- --gray-50: #303030;
- --gray-100: #404040;
- --gray-200: #525252;
- --gray-700: #dbdbdb;
- --gray-900: #fafafa;
+ --gray-10: #1f1e24;
+ --gray-50: #333238;
+ --gray-100: #434248;
+ --gray-200: #535158;
+ --gray-700: #bfbfc3;
+ --gray-900: #ececef;
--green-100: #0d532a;
--green-700: #91d4a8;
- --gl-text-color: #fafafa;
+ --gl-text-color: #ececef;
--border-color: #4f4f4f;
--black: #fff;
}
@@ -42,9 +42,9 @@ body {
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
- color: #fafafa;
+ color: #ececef;
text-align: left;
- background-color: #1f1f1f;
+ background-color: #1f1e24;
}
ul {
margin-top: 0;
@@ -118,7 +118,7 @@ kbd {
padding: 0.2rem 0.4rem;
font-size: 90%;
color: #333;
- background-color: #fafafa;
+ background-color: #ececef;
border-radius: 0.2rem;
}
kbd kbd {
@@ -141,24 +141,24 @@ kbd kbd {
font-size: 0.875rem;
font-weight: 400;
line-height: 1.5;
- color: #fafafa;
+ color: #ececef;
background-color: #333;
background-clip: padding-box;
- border: 1px solid #868686;
+ border: 1px solid #737278;
border-radius: 0.25rem;
}
@media (prefers-reduced-motion: reduce) {
}
.form-control:-moz-focusring {
color: transparent;
- text-shadow: 0 0 0 #fafafa;
+ text-shadow: 0 0 0 #ececef;
}
.form-control::placeholder {
- color: #bfbfbf;
+ color: #a4a3a8;
opacity: 1;
}
.form-control:disabled {
- background-color: #303030;
+ background-color: #333238;
opacity: 1;
}
.form-inline {
@@ -176,7 +176,7 @@ kbd kbd {
.btn {
display: inline-block;
font-weight: 400;
- color: #fafafa;
+ color: #ececef;
text-align: center;
vertical-align: middle;
user-select: none;
@@ -212,7 +212,7 @@ kbd kbd {
padding: 0.5rem 0;
margin: 0.125rem 0 0;
font-size: 1rem;
- color: #fafafa;
+ color: #ececef;
text-align: left;
list-style: none;
background-color: #333;
@@ -319,15 +319,15 @@ kbd kbd {
border-radius: 10rem;
}
.badge-success {
- color: #fff;
+ color: #fbfafd;
background-color: #2da160;
}
.badge-info {
- color: #fff;
+ color: #fbfafd;
background-color: #428fdc;
}
.badge-warning {
- color: #fff;
+ color: #fbfafd;
background-color: #c17d10;
}
.rounded-circle {
@@ -371,7 +371,7 @@ kbd kbd {
.gl-avatar {
border-width: 1px;
border-style: solid;
- border-color: rgba(0, 0, 0, 0.08);
+ border-color: rgba(251, 250, 253, 0.08);
overflow: hidden;
flex-shrink: 0;
}
@@ -455,8 +455,8 @@ a.gl-badge.badge-warning:active {
padding-left: 0.75rem;
padding-right: 0.75rem;
height: auto;
- color: #fafafa;
- box-shadow: inset 0 0 0 1px #868686;
+ color: #ececef;
+ box-shadow: inset 0 0 0 1px #737278;
border-style: none;
appearance: none;
-moz-appearance: none;
@@ -465,17 +465,17 @@ a.gl-badge.badge-warning:active {
.gl-form-input:not(.form-control-plaintext):not([type="color"]):read-only,
.gl-form-input.form-control:disabled,
.gl-form-input.form-control:not(.form-control-plaintext):not([type="color"]):read-only {
- background-color: #1f1f1f;
- box-shadow: inset 0 0 0 1px #404040;
+ background-color: #1f1e24;
+ box-shadow: inset 0 0 0 1px #434248;
}
.gl-form-input:disabled,
.gl-form-input.form-control:disabled {
cursor: not-allowed;
- color: #999;
+ color: #89888d;
}
.gl-form-input::placeholder,
.gl-form-input.form-control::placeholder {
- color: #868686;
+ color: #737278;
}
.gl-icon {
fill: currentColor;
@@ -518,9 +518,9 @@ a.gl-badge.badge-warning:active {
padding-right: 0.75rem;
background-color: transparent;
line-height: 1rem;
- color: #fafafa;
+ color: #ececef;
fill: currentColor;
- box-shadow: inset 0 0 0 1px #525252;
+ box-shadow: inset 0 0 0 1px #535158;
justify-content: center;
align-items: center;
font-size: 0.875rem;
@@ -531,20 +531,20 @@ a.gl-badge.badge-warning:active {
}
.gl-button.gl-button.btn-default:active,
.gl-button.gl-button.btn-default.active {
- box-shadow: inset 0 0 0 1px #bfbfbf, 0 0 0 1px #333, 0 0 0 3px #1f75cb;
+ box-shadow: inset 0 0 0 1px #a4a3a8, 0 0 0 1px #333, 0 0 0 3px #1f75cb;
outline: none;
- background-color: #404040;
+ background-color: #434248;
}
.gl-button.gl-button.btn-default:active .gl-icon,
.gl-button.gl-button.btn-default.active .gl-icon {
- color: #fafafa;
+ color: #ececef;
}
.gl-button.gl-button.btn-default .gl-icon {
- color: #999;
+ color: #89888d;
}
.gl-search-box-by-type-search-icon {
margin: 0.5rem;
- color: #999;
+ color: #89888d;
width: 1rem;
position: absolute;
}
@@ -594,11 +594,11 @@ svg {
height: 0;
margin: 4px 0;
overflow: hidden;
- border-top: 1px solid #404040;
+ border-top: 1px solid #434248;
}
.toggle-sidebar-button .collapse-text,
.toggle-sidebar-button .icon-chevron-double-lg-left {
- color: #999;
+ color: #89888d;
}
html {
overflow-y: scroll;
@@ -614,20 +614,20 @@ html {
font-weight: 400;
padding: 6px 10px;
background-color: #333;
- border-color: #404040;
- color: #fafafa;
- color: #fafafa;
+ border-color: #434248;
+ color: #ececef;
+ color: #ececef;
white-space: nowrap;
}
.btn:active {
- background-color: #303030;
+ background-color: #333238;
box-shadow: none;
}
.btn:active,
.btn.active {
background-color: #444;
border-color: #4f4f4f;
- color: #fafafa;
+ color: #ececef;
}
.btn svg {
height: 15px;
@@ -639,7 +639,7 @@ html {
.badge.badge-pill:not(.gl-badge) {
font-weight: 400;
background-color: rgba(255, 255, 255, 0.07);
- color: #dbdbdb;
+ color: #bfbfc3;
vertical-align: baseline;
}
.gl-font-sm {
@@ -658,10 +658,10 @@ html {
.dropdown-menu-toggle {
padding: 6px 8px 6px 10px;
background-color: #333;
- color: #fafafa;
+ color: #ececef;
font-size: 14px;
text-align: left;
- border: 1px solid #404040;
+ border: 1px solid #434248;
border-radius: 0.25rem;
white-space: nowrap;
}
@@ -690,7 +690,7 @@ html {
font-weight: 400;
padding: 8px 0;
background-color: #333;
- border: 1px solid #404040;
+ border: 1px solid #434248;
border-radius: 0.25rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
@@ -713,7 +713,7 @@ html {
font-weight: 400;
position: relative;
padding: 8px 12px;
- color: #fafafa;
+ color: #ececef;
line-height: 16px;
white-space: normal;
overflow: hidden;
@@ -723,7 +723,7 @@ html {
.dropdown-menu li > a:active,
.dropdown-menu li button:active {
background-color: #4f4f4f;
- color: #fafafa;
+ color: #ececef;
outline: 0;
text-decoration: none;
}
@@ -737,7 +737,7 @@ html {
height: 1px;
margin: 0.25rem 0;
padding: 0;
- background-color: #404040;
+ background-color: #434248;
}
.dropdown-menu .badge.badge-pill + span:not(.badge):not(.badge-pill) {
margin-right: 40px;
@@ -764,7 +764,7 @@ html {
}
input {
border-radius: 0.25rem;
- color: #fafafa;
+ color: #ececef;
background-color: #333;
}
.form-control {
@@ -772,23 +772,23 @@ input {
padding: 6px 10px;
}
.form-control::placeholder {
- color: #868686;
+ color: #737278;
}
kbd {
display: inline-block;
padding: 3px 5px;
font-size: 0.6875rem;
line-height: 10px;
- color: var(--gray-700, #dbdbdb);
+ color: var(--gray-700, #bfbfc3);
vertical-align: middle;
- background-color: var(--gray-10, #1f1f1f);
+ background-color: var(--gray-10, #1f1e24);
border-width: 1px;
border-style: solid;
- border-color: var(--gray-100, #404040) var(--gray-100, #404040)
- var(--gray-200, #525252);
+ border-color: var(--gray-100, #434248) var(--gray-100, #434248)
+ var(--gray-200, #535158);
border-image: none;
border-radius: 3px;
- box-shadow: 0 -1px 0 var(--gray-200, #525252) inset;
+ box-shadow: 0 -1px 0 var(--gray-200, #535158) inset;
}
.navbar-gitlab {
padding: 0 16px;
@@ -1042,7 +1042,7 @@ kbd {
width: 100%;
align-items: center;
padding: 10px 16px 10px 10px;
- color: #fafafa;
+ color: #ececef;
background-color: transparent;
border: 0;
text-align: left;
@@ -1054,7 +1054,7 @@ kbd {
.context-header .sidebar-context-title {
overflow: hidden;
text-overflow: ellipsis;
- color: #fafafa;
+ color: #ececef;
}
@media (min-width: 768px) {
.page-with-contextual-sidebar {
@@ -1078,7 +1078,7 @@ kbd {
z-index: 600;
width: 256px;
top: var(--header-height, 48px);
- background-color: #f5f5f5;
+ background-color: #1f1e24;
border-right: 1px solid #e9e9e9;
transform: translate3d(0, 0, 0);
}
@@ -1115,7 +1115,7 @@ kbd {
}
.nav-sidebar a {
text-decoration: none;
- color: #fafafa;
+ color: #ececef;
}
.nav-sidebar li {
white-space: nowrap;
@@ -1400,7 +1400,7 @@ kbd {
display: block;
}
.sidebar-top-level-items li > a.gl-link {
- color: #fafafa;
+ color: #ececef;
}
.sidebar-top-level-items li > a.gl-link:active {
text-decoration: none;
@@ -1417,12 +1417,12 @@ kbd {
.close-nav-button {
height: 48px;
padding: 0 16px;
- background-color: #303030;
+ background-color: #333238;
border: 0;
- color: #999;
+ color: #89888d;
display: flex;
align-items: center;
- background-color: #f5f5f5;
+ background-color: #1f1e24;
position: fixed;
bottom: 0;
width: 255px;
@@ -1493,14 +1493,14 @@ kbd {
}
}
input::-moz-placeholder {
- color: #868686;
+ color: #737278;
opacity: 1;
}
input::-ms-input-placeholder {
- color: #868686;
+ color: #737278;
}
input:-ms-input-placeholder {
- color: #868686;
+ color: #737278;
}
svg {
fill: currentColor;
@@ -1629,7 +1629,7 @@ svg.s16 {
padding: 0;
background: #222;
overflow: hidden;
- box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
+ box-shadow: inset 0 0 0 1px rgba(251, 250, 253, 0.1);
}
.avatar.avatar-tile {
border-radius: 0;
@@ -1638,8 +1638,8 @@ svg.s16 {
.identicon {
text-align: center;
vertical-align: top;
- color: #fafafa;
- background-color: #303030;
+ color: #ececef;
+ background-color: #333238;
}
.identicon.s16 {
font-size: 10px;
@@ -1668,7 +1668,7 @@ svg.s16 {
background-color: #5c2900;
}
.identicon.bg7 {
- background-color: #303030;
+ background-color: #333238;
}
.avatar-container {
overflow: hidden;
@@ -1707,18 +1707,18 @@ svg.s16 {
color-scheme: dark;
}
body.gl-dark {
- --gray-10: #1f1f1f;
- --gray-50: #303030;
- --gray-100: #404040;
- --gray-200: #525252;
- --gray-300: #5e5e5e;
- --gray-400: #868686;
- --gray-500: #999;
- --gray-600: #bfbfbf;
- --gray-700: #dbdbdb;
- --gray-800: #f0f0f0;
- --gray-900: #fafafa;
- --gray-950: #fff;
+ --gray-10: #1f1e24;
+ --gray-50: #333238;
+ --gray-100: #434248;
+ --gray-200: #535158;
+ --gray-300: #626168;
+ --gray-400: #737278;
+ --gray-500: #89888d;
+ --gray-600: #a4a3a8;
+ --gray-700: #bfbfc3;
+ --gray-800: #dcdcde;
+ --gray-900: #ececef;
+ --gray-950: #fbfafd;
--green-50: #0a4020;
--green-100: #0d532a;
--green-200: #24663b;
@@ -1790,59 +1790,59 @@ body.gl-dark {
--dark-icon-color-purple-3: #9a79f7;
--dark-icon-color-orange-1: #665349;
--dark-icon-color-orange-2: #b37a5d;
- --gl-text-color: #fafafa;
+ --gl-text-color: #ececef;
--border-color: #4f4f4f;
--white: #333;
--black: #fff;
- --gray-light: #303030;
+ --gray-light: #333238;
--svg-status-bg: #333;
}
.nav-sidebar,
.toggle-sidebar-button,
.close-nav-button {
- background-color: #262626;
- border-right: 1px solid #303030;
+ background-color: #29282d;
+ border-right: 1px solid #333238;
}
.gl-avatar:not(.gl-avatar-identicon),
.avatar-container,
.avatar {
- background: rgba(255, 255, 255, 0.04);
+ background: rgba(251, 250, 253, 0.04);
}
.gl-avatar {
border-style: none;
- box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
+ box-shadow: inset 0 0 0 1px rgba(251, 250, 253, 0.1);
}
body.gl-dark {
- --gl-theme-accent: #868686;
+ --gl-theme-accent: #737278;
}
body.gl-dark .navbar-gitlab {
- background-color: #fafafa;
+ background-color: #ececef;
}
body.gl-dark .navbar-gitlab .navbar-collapse {
- color: #fafafa;
+ color: #ececef;
}
body.gl-dark .navbar-gitlab .container-fluid .navbar-toggler {
- border-left: 1px solid #b3b3b3;
- color: #fafafa;
+ border-left: 1px solid #a3a2a6;
+ color: #ececef;
}
body.gl-dark .navbar-gitlab .navbar-sub-nav > li.active > a,
body.gl-dark .navbar-gitlab .navbar-sub-nav > li.active > button,
body.gl-dark .navbar-gitlab .navbar-nav > li.active > a,
body.gl-dark .navbar-gitlab .navbar-nav > li.active > button {
- color: #fafafa;
+ color: #ececef;
background-color: #333;
}
body.gl-dark .navbar-gitlab .navbar-sub-nav {
- color: #fafafa;
+ color: #ececef;
}
body.gl-dark .navbar-gitlab .nav > li {
- color: #fafafa;
+ color: #ececef;
}
body.gl-dark .navbar-gitlab .nav > li.header-search-new {
- color: #fafafa;
+ color: #ececef;
}
body.gl-dark .navbar-gitlab .nav > li > a .notification-dot {
- border: 2px solid #fafafa;
+ border: 2px solid #ececef;
}
body.gl-dark
.navbar-gitlab
@@ -1850,7 +1850,7 @@ body.gl-dark
> li
> a.header-help-dropdown-toggle
.notification-dot {
- background-color: #fafafa;
+ background-color: #ececef;
}
body.gl-dark
.navbar-gitlab
@@ -1858,10 +1858,10 @@ body.gl-dark
> li
> a.header-user-dropdown-toggle
.header-user-avatar {
- border-color: #fafafa;
+ border-color: #ececef;
}
body.gl-dark .navbar-gitlab .nav > li.active > a {
- color: #fafafa;
+ color: #ececef;
background-color: #333;
}
body.gl-dark .navbar-gitlab .nav > li.active > a .notification-dot {
@@ -1873,48 +1873,48 @@ body.gl-dark
> li.active
> a.header-help-dropdown-toggle
.notification-dot {
- background-color: #fafafa;
+ background-color: #ececef;
}
body.gl-dark .header-search {
- background-color: rgba(250, 250, 250, 0.2) !important;
+ background-color: rgba(236, 236, 239, 0.2) !important;
border-radius: 4px;
}
body.gl-dark .header-search svg.gl-search-box-by-type-search-icon {
- color: rgba(250, 250, 250, 0.8);
+ color: rgba(236, 236, 239, 0.8);
}
body.gl-dark .header-search input {
background-color: transparent;
- color: rgba(250, 250, 250, 0.8);
- box-shadow: inset 0 0 0 1px rgba(250, 250, 250, 0.4);
+ color: rgba(236, 236, 239, 0.8);
+ box-shadow: inset 0 0 0 1px rgba(236, 236, 239, 0.4);
}
body.gl-dark .header-search input::placeholder {
- color: rgba(250, 250, 250, 0.8);
+ color: rgba(236, 236, 239, 0.8);
}
body.gl-dark .header-search input:active::placeholder {
- color: #868686;
+ color: #737278;
}
body.gl-dark .header-search .keyboard-shortcut-helper {
- color: #fafafa;
- background-color: rgba(250, 250, 250, 0.2);
+ color: #ececef;
+ background-color: rgba(236, 236, 239, 0.2);
}
body.gl-dark .search form {
- background-color: rgba(250, 250, 250, 0.2);
+ background-color: rgba(236, 236, 239, 0.2);
}
body.gl-dark .search .search-input::placeholder {
- color: rgba(250, 250, 250, 0.8);
+ color: rgba(236, 236, 239, 0.8);
}
body.gl-dark .search .search-input-wrap .search-icon,
body.gl-dark .search .search-input-wrap .clear-icon {
- fill: rgba(250, 250, 250, 0.8);
+ fill: rgba(236, 236, 239, 0.8);
}
body.gl-dark .nav-sidebar li.active > a {
- color: #fafafa;
+ color: #ececef;
}
body.gl-dark .nav-sidebar .fly-out-top-item a,
body.gl-dark .nav-sidebar .fly-out-top-item.active a,
body.gl-dark .nav-sidebar .fly-out-top-item .fly-out-top-item-container {
- background-color: var(--gray-100, #303030);
- color: var(--gray-900, #fafafa);
+ background-color: var(--gray-100, #333238);
+ color: var(--gray-900, #ececef);
}
body.gl-dark .navbar-gitlab {
background-color: var(--gray-50);
@@ -1951,18 +1951,18 @@ body.gl-dark .navbar-gitlab .search form .search-input {
color-scheme: dark;
}
body.gl-dark {
- --gray-10: #1f1f1f;
- --gray-50: #303030;
- --gray-100: #404040;
- --gray-200: #525252;
- --gray-300: #5e5e5e;
- --gray-400: #868686;
- --gray-500: #999;
- --gray-600: #bfbfbf;
- --gray-700: #dbdbdb;
- --gray-800: #f0f0f0;
- --gray-900: #fafafa;
- --gray-950: #fff;
+ --gray-10: #1f1e24;
+ --gray-50: #333238;
+ --gray-100: #434248;
+ --gray-200: #535158;
+ --gray-300: #626168;
+ --gray-400: #737278;
+ --gray-500: #89888d;
+ --gray-600: #a4a3a8;
+ --gray-700: #bfbfc3;
+ --gray-800: #dcdcde;
+ --gray-900: #ececef;
+ --gray-950: #fbfafd;
--green-50: #0a4020;
--green-100: #0d532a;
--green-200: #24663b;
@@ -2034,11 +2034,11 @@ body.gl-dark {
--dark-icon-color-purple-3: #9a79f7;
--dark-icon-color-orange-1: #665349;
--dark-icon-color-orange-2: #b37a5d;
- --gl-text-color: #fafafa;
+ --gl-text-color: #ececef;
--border-color: #4f4f4f;
--white: #333;
--black: #fff;
- --gray-light: #303030;
+ --gray-light: #333238;
--svg-status-bg: #333;
}
.tab-width-8 {
diff --git a/app/assets/stylesheets/startup/startup-general.scss b/app/assets/stylesheets/startup/startup-general.scss
index 802262ba346..7fb373bb6f4 100644
--- a/app/assets/stylesheets/startup/startup-general.scss
+++ b/app/assets/stylesheets/startup/startup-general.scss
@@ -23,7 +23,7 @@ body {
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
- color: #303030;
+ color: #333238;
text-align: left;
background-color: #fff;
}
@@ -99,7 +99,7 @@ kbd {
padding: 0.2rem 0.4rem;
font-size: 90%;
color: #fff;
- background-color: #303030;
+ background-color: #333238;
border-radius: 0.2rem;
}
kbd kbd {
@@ -122,24 +122,24 @@ kbd kbd {
font-size: 0.875rem;
font-weight: 400;
line-height: 1.5;
- color: #303030;
+ color: #333238;
background-color: #fff;
background-clip: padding-box;
- border: 1px solid #868686;
+ border: 1px solid #89888d;
border-radius: 0.25rem;
}
@media (prefers-reduced-motion: reduce) {
}
.form-control:-moz-focusring {
color: transparent;
- text-shadow: 0 0 0 #303030;
+ text-shadow: 0 0 0 #333238;
}
.form-control::placeholder {
- color: #5e5e5e;
+ color: #626168;
opacity: 1;
}
.form-control:disabled {
- background-color: #fafafa;
+ background-color: #fbfafd;
opacity: 1;
}
.form-inline {
@@ -157,7 +157,7 @@ kbd kbd {
.btn {
display: inline-block;
font-weight: 400;
- color: #303030;
+ color: #333238;
text-align: center;
vertical-align: middle;
user-select: none;
@@ -193,7 +193,7 @@ kbd kbd {
padding: 0.5rem 0;
margin: 0.125rem 0 0;
font-size: 1rem;
- color: #303030;
+ color: #333238;
text-align: left;
list-style: none;
background-color: #fff;
@@ -352,7 +352,7 @@ kbd kbd {
.gl-avatar {
border-width: 1px;
border-style: solid;
- border-color: rgba(0, 0, 0, 0.08);
+ border-color: rgba(31, 30, 36, 0.08);
overflow: hidden;
flex-shrink: 0;
}
@@ -436,8 +436,8 @@ a.gl-badge.badge-warning:active {
padding-left: 0.75rem;
padding-right: 0.75rem;
height: auto;
- color: #303030;
- box-shadow: inset 0 0 0 1px #868686;
+ color: #333238;
+ box-shadow: inset 0 0 0 1px #89888d;
border-style: none;
appearance: none;
-moz-appearance: none;
@@ -446,17 +446,17 @@ a.gl-badge.badge-warning:active {
.gl-form-input:not(.form-control-plaintext):not([type="color"]):read-only,
.gl-form-input.form-control:disabled,
.gl-form-input.form-control:not(.form-control-plaintext):not([type="color"]):read-only {
- background-color: #f5f5f5;
- box-shadow: inset 0 0 0 1px #dbdbdb;
+ background-color: #fbfafd;
+ box-shadow: inset 0 0 0 1px #dcdcde;
}
.gl-form-input:disabled,
.gl-form-input.form-control:disabled {
cursor: not-allowed;
- color: #666;
+ color: #737278;
}
.gl-form-input::placeholder,
.gl-form-input.form-control::placeholder {
- color: #868686;
+ color: #89888d;
}
.gl-icon {
fill: currentColor;
@@ -499,9 +499,9 @@ a.gl-badge.badge-warning:active {
padding-right: 0.75rem;
background-color: transparent;
line-height: 1rem;
- color: #303030;
+ color: #333238;
fill: currentColor;
- box-shadow: inset 0 0 0 1px #bfbfbf;
+ box-shadow: inset 0 0 0 1px #bfbfc3;
justify-content: center;
align-items: center;
font-size: 0.875rem;
@@ -512,20 +512,20 @@ a.gl-badge.badge-warning:active {
}
.gl-button.gl-button.btn-default:active,
.gl-button.gl-button.btn-default.active {
- box-shadow: inset 0 0 0 1px #5e5e5e, 0 0 0 1px #fff, 0 0 0 3px #428fdc;
+ box-shadow: inset 0 0 0 1px #626168, 0 0 0 1px #fff, 0 0 0 3px #428fdc;
outline: none;
- background-color: #dbdbdb;
+ background-color: #dcdcde;
}
.gl-button.gl-button.btn-default:active .gl-icon,
.gl-button.gl-button.btn-default.active .gl-icon {
- color: #303030;
+ color: #333238;
}
.gl-button.gl-button.btn-default .gl-icon {
- color: #666;
+ color: #737278;
}
.gl-search-box-by-type-search-icon {
margin: 0.5rem;
- color: #666;
+ color: #737278;
width: 1rem;
position: absolute;
}
@@ -575,11 +575,11 @@ svg {
height: 0;
margin: 4px 0;
overflow: hidden;
- border-top: 1px solid #dbdbdb;
+ border-top: 1px solid #dcdcde;
}
.toggle-sidebar-button .collapse-text,
.toggle-sidebar-button .icon-chevron-double-lg-left {
- color: #666;
+ color: #737278;
}
html {
overflow-y: scroll;
@@ -595,20 +595,20 @@ html {
font-weight: 400;
padding: 6px 10px;
background-color: #fff;
- border-color: #dbdbdb;
- color: #303030;
- color: #303030;
+ border-color: #dcdcde;
+ color: #333238;
+ color: #333238;
white-space: nowrap;
}
.btn:active {
- background-color: #f0f0f0;
+ background-color: #ececef;
box-shadow: none;
}
.btn:active,
.btn.active {
background-color: #eaeaea;
border-color: #e3e3e3;
- color: #303030;
+ color: #333238;
}
.btn svg {
height: 15px;
@@ -620,7 +620,7 @@ html {
.badge.badge-pill:not(.gl-badge) {
font-weight: 400;
background-color: rgba(0, 0, 0, 0.07);
- color: #525252;
+ color: #535158;
vertical-align: baseline;
}
.gl-font-sm {
@@ -639,10 +639,10 @@ html {
.dropdown-menu-toggle {
padding: 6px 8px 6px 10px;
background-color: #fff;
- color: #303030;
+ color: #333238;
font-size: 14px;
text-align: left;
- border: 1px solid #dbdbdb;
+ border: 1px solid #dcdcde;
border-radius: 0.25rem;
white-space: nowrap;
}
@@ -671,7 +671,7 @@ html {
font-weight: 400;
padding: 8px 0;
background-color: #fff;
- border: 1px solid #dbdbdb;
+ border: 1px solid #dcdcde;
border-radius: 0.25rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
@@ -694,7 +694,7 @@ html {
font-weight: 400;
position: relative;
padding: 8px 12px;
- color: #303030;
+ color: #333238;
line-height: 16px;
white-space: normal;
overflow: hidden;
@@ -703,8 +703,8 @@ html {
}
.dropdown-menu li > a:active,
.dropdown-menu li button:active {
- background-color: #eee;
- color: #303030;
+ background-color: #ececef;
+ color: #333238;
outline: 0;
text-decoration: none;
}
@@ -718,7 +718,7 @@ html {
height: 1px;
margin: 0.25rem 0;
padding: 0;
- background-color: #dbdbdb;
+ background-color: #dcdcde;
}
.dropdown-menu .badge.badge-pill + span:not(.badge):not(.badge-pill) {
margin-right: 40px;
@@ -745,7 +745,7 @@ html {
}
input {
border-radius: 0.25rem;
- color: #303030;
+ color: #333238;
background-color: #fff;
}
.form-control {
@@ -753,23 +753,23 @@ input {
padding: 6px 10px;
}
.form-control::placeholder {
- color: #868686;
+ color: #89888d;
}
kbd {
display: inline-block;
padding: 3px 5px;
font-size: 0.6875rem;
line-height: 10px;
- color: var(--gray-700, #525252);
+ color: var(--gray-700, #535158);
vertical-align: middle;
- background-color: var(--gray-10, #f5f5f5);
+ background-color: var(--gray-10, #fbfafd);
border-width: 1px;
border-style: solid;
- border-color: var(--gray-100, #dbdbdb) var(--gray-100, #dbdbdb)
- var(--gray-200, #bfbfbf);
+ border-color: var(--gray-100, #dcdcde) var(--gray-100, #dcdcde)
+ var(--gray-200, #bfbfc3);
border-image: none;
border-radius: 3px;
- box-shadow: 0 -1px 0 var(--gray-200, #bfbfbf) inset;
+ box-shadow: 0 -1px 0 var(--gray-200, #bfbfc3) inset;
}
.navbar-gitlab {
padding: 0 16px;
@@ -991,7 +991,7 @@ kbd {
float: left;
margin-right: 5px;
border-radius: 50%;
- border: 1px solid #f5f5f5;
+ border: 1px solid #f2f2f4;
}
.notification-dot {
background-color: #d99530;
@@ -1023,7 +1023,7 @@ kbd {
width: 100%;
align-items: center;
padding: 10px 16px 10px 10px;
- color: #303030;
+ color: #333238;
background-color: transparent;
border: 0;
text-align: left;
@@ -1035,7 +1035,7 @@ kbd {
.context-header .sidebar-context-title {
overflow: hidden;
text-overflow: ellipsis;
- color: #303030;
+ color: #333238;
}
@media (min-width: 768px) {
.page-with-contextual-sidebar {
@@ -1059,7 +1059,7 @@ kbd {
z-index: 600;
width: 256px;
top: var(--header-height, 48px);
- background-color: #f5f5f5;
+ background-color: #fbfafd;
border-right: 1px solid #e9e9e9;
transform: translate3d(0, 0, 0);
}
@@ -1096,7 +1096,7 @@ kbd {
}
.nav-sidebar a {
text-decoration: none;
- color: #303030;
+ color: #333238;
}
.nav-sidebar li {
white-space: nowrap;
@@ -1381,7 +1381,7 @@ kbd {
display: block;
}
.sidebar-top-level-items li > a.gl-link {
- color: #303030;
+ color: #333238;
}
.sidebar-top-level-items li > a.gl-link:active {
text-decoration: none;
@@ -1398,12 +1398,12 @@ kbd {
.close-nav-button {
height: 48px;
padding: 0 16px;
- background-color: #fafafa;
+ background-color: #fbfafd;
border: 0;
- color: #666;
+ color: #737278;
display: flex;
align-items: center;
- background-color: #f5f5f5;
+ background-color: #fbfafd;
position: fixed;
bottom: 0;
width: 255px;
@@ -1474,14 +1474,14 @@ kbd {
}
}
input::-moz-placeholder {
- color: #868686;
+ color: #89888d;
opacity: 1;
}
input::-ms-input-placeholder {
- color: #868686;
+ color: #89888d;
}
input:-ms-input-placeholder {
- color: #868686;
+ color: #89888d;
}
svg {
fill: currentColor;
@@ -1608,9 +1608,9 @@ svg.s16 {
width: 40px;
height: 40px;
padding: 0;
- background: #fdfdfd;
+ background: #fefefe;
overflow: hidden;
- box-shadow: inset 0 0 0 1px rgba(31, 31, 31, 0.1);
+ box-shadow: inset 0 0 0 1px rgba(31, 30, 36, 0.1);
}
.avatar.avatar-tile {
border-radius: 0;
@@ -1619,8 +1619,8 @@ svg.s16 {
.identicon {
text-align: center;
vertical-align: top;
- color: #303030;
- background-color: #f0f0f0;
+ color: #333238;
+ background-color: #ececef;
}
.identicon.s16 {
font-size: 10px;
@@ -1649,7 +1649,7 @@ svg.s16 {
background-color: #fdf1dd;
}
.identicon.bg7 {
- background-color: #f0f0f0;
+ background-color: #ececef;
}
.avatar-container {
overflow: hidden;
diff --git a/app/assets/stylesheets/startup/startup-signin.scss b/app/assets/stylesheets/startup/startup-signin.scss
index 33e10b9bd62..7ae158b3930 100644
--- a/app/assets/stylesheets/startup/startup-signin.scss
+++ b/app/assets/stylesheets/startup/startup-signin.scss
@@ -22,7 +22,7 @@ body {
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
- color: #303030;
+ color: #333238;
text-align: left;
background-color: #fff;
}
@@ -110,7 +110,7 @@ h3 {
margin-bottom: 0.25rem;
font-weight: 600;
line-height: 1.2;
- color: #303030;
+ color: #333238;
}
h1 {
font-size: 2.1875rem;
@@ -196,24 +196,24 @@ hr {
font-size: 0.875rem;
font-weight: 400;
line-height: 1.5;
- color: #303030;
+ color: #333238;
background-color: #fff;
background-clip: padding-box;
- border: 1px solid #868686;
+ border: 1px solid #89888d;
border-radius: 0.25rem;
}
@media (prefers-reduced-motion: reduce) {
}
.form-control:-moz-focusring {
color: transparent;
- text-shadow: 0 0 0 #303030;
+ text-shadow: 0 0 0 #333238;
}
.form-control::placeholder {
- color: #5e5e5e;
+ color: #626168;
opacity: 1;
}
.form-control:disabled {
- background-color: #fafafa;
+ background-color: #fbfafd;
opacity: 1;
}
.form-group {
@@ -222,7 +222,7 @@ hr {
.btn {
display: inline-block;
font-weight: 400;
- color: #303030;
+ color: #333238;
text-align: center;
vertical-align: middle;
user-select: none;
@@ -282,10 +282,10 @@ input.btn-block[type="button"] {
border-color: #b3d7ff;
}
.custom-control-input:disabled ~ .custom-control-label {
- color: #5e5e5e;
+ color: #626168;
}
.custom-control-input:disabled ~ .custom-control-label::before {
- background-color: #fafafa;
+ background-color: #fbfafd;
}
.custom-control-label {
position: relative;
@@ -302,7 +302,7 @@ input.btn-block[type="button"] {
pointer-events: none;
content: "";
background-color: #fff;
- border: #666 solid 1px;
+ border: #737278 solid 1px;
}
.custom-control-label::after {
position: absolute;
@@ -400,8 +400,8 @@ input.btn-block[type="button"] {
padding-left: 0.75rem;
padding-right: 0.75rem;
height: auto;
- color: #303030;
- box-shadow: inset 0 0 0 1px #868686;
+ color: #333238;
+ box-shadow: inset 0 0 0 1px #89888d;
border-style: none;
appearance: none;
-moz-appearance: none;
@@ -410,27 +410,27 @@ input.btn-block[type="button"] {
.gl-form-input:not(.form-control-plaintext):not([type="color"]):read-only,
.gl-form-input.form-control:disabled,
.gl-form-input.form-control:not(.form-control-plaintext):not([type="color"]):read-only {
- background-color: #f5f5f5;
- box-shadow: inset 0 0 0 1px #dbdbdb;
+ background-color: #fbfafd;
+ box-shadow: inset 0 0 0 1px #dcdcde;
}
.gl-form-input:disabled,
.gl-form-input.form-control:disabled {
cursor: not-allowed;
- color: #666;
+ color: #737278;
}
.gl-form-input::placeholder,
.gl-form-input.form-control::placeholder {
- color: #868686;
+ color: #89888d;
}
.gl-form-checkbox {
font-size: 0.875rem;
line-height: 1rem;
- color: #303030;
+ color: #333238;
}
.gl-form-checkbox .custom-control-input:disabled,
.gl-form-checkbox .custom-control-input:disabled ~ .custom-control-label {
cursor: not-allowed;
- color: #868686;
+ color: #89888d;
}
.gl-form-checkbox.custom-control .custom-control-input ~ .custom-control-label {
cursor: pointer;
@@ -447,7 +447,7 @@ input.btn-block[type="button"] {
.custom-control-input
~ .custom-control-label::before {
background-color: #fff;
- border-color: #868686;
+ border-color: #89888d;
}
.gl-form-checkbox.custom-control
.custom-control-input:checked
@@ -490,8 +490,8 @@ input.btn-block[type="button"] {
.gl-form-checkbox.custom-control
.custom-control-input:disabled
~ .custom-control-label::before {
- background-color: #f0f0f0;
- border-color: #dbdbdb;
+ background-color: #ececef;
+ border-color: #dcdcde;
pointer-events: auto;
}
.gl-form-checkbox.custom-control
@@ -500,8 +500,8 @@ input.btn-block[type="button"] {
.gl-form-checkbox.custom-control
.custom-control-input[type="checkbox"]:indeterminate:disabled
~ .custom-control-label::before {
- background-color: #dbdbdb;
- border-color: #dbdbdb;
+ background-color: #dcdcde;
+ border-color: #dcdcde;
}
.gl-form-checkbox.custom-control
.custom-control-input:checked:disabled
@@ -509,7 +509,7 @@ input.btn-block[type="button"] {
.gl-form-checkbox.custom-control
.custom-control-input[type="checkbox"]:indeterminate:disabled
~ .custom-control-label::after {
- background-color: #5e5e5e;
+ background-color: #626168;
}
.gl-button {
display: inline-flex;
@@ -526,9 +526,9 @@ input.btn-block[type="button"] {
padding-right: 0.75rem;
background-color: transparent;
line-height: 1rem;
- color: #303030;
+ color: #333238;
fill: currentColor;
- box-shadow: inset 0 0 0 1px #bfbfbf;
+ box-shadow: inset 0 0 0 1px #bfbfc3;
justify-content: center;
align-items: center;
font-size: 0.875rem;
@@ -560,9 +560,9 @@ input.btn-block[type="button"] {
.gl-button.gl-button.btn-default.active,
.gl-button.gl-button.btn-block.btn-default:active,
.gl-button.gl-button.btn-block.btn-default.active {
- box-shadow: inset 0 0 0 1px #5e5e5e, 0 0 0 1px #fff, 0 0 0 3px #428fdc;
+ box-shadow: inset 0 0 0 1px #626168, 0 0 0 1px #fff, 0 0 0 3px #428fdc;
outline: none;
- background-color: #dbdbdb;
+ background-color: #dcdcde;
}
.gl-button.gl-button.btn-confirm,
.gl-button.gl-button.btn-block.btn-confirm {
@@ -636,20 +636,20 @@ body.navless {
font-weight: 400;
padding: 6px 10px;
background-color: #fff;
- border-color: #dbdbdb;
- color: #303030;
- color: #303030;
+ border-color: #dcdcde;
+ color: #333238;
+ color: #333238;
white-space: nowrap;
}
.btn:active {
- background-color: #f0f0f0;
+ background-color: #ececef;
box-shadow: none;
}
.btn:active,
.btn.active {
background-color: #eaeaea;
border-color: #e3e3e3;
- color: #303030;
+ color: #333238;
}
.btn svg {
height: 15px;
@@ -676,7 +676,7 @@ body.navless {
}
hr {
margin: 1.5rem 0;
- border-top: 1px solid #eee;
+ border-top: 1px solid #ececef;
}
.footer-links {
margin-bottom: 20px;
@@ -704,7 +704,7 @@ hr {
}
input {
border-radius: 0.25rem;
- color: #303030;
+ color: #333238;
background-color: #fff;
}
label {
@@ -721,7 +721,7 @@ label.label-bold {
padding: 6px 10px;
}
.form-control::placeholder {
- color: #868686;
+ color: #89888d;
}
.gl-show-field-errors .form-control:not(textarea) {
height: 34px;
@@ -730,7 +730,7 @@ label.label-bold {
justify-content: center;
height: var(--header-height, 48px);
background: #fff;
- border-bottom: 1px solid #dbdbdb;
+ border-bottom: 1px solid #dcdcde;
}
.navbar-empty .tanuki-logo,
.navbar-empty .brand-header-logo {
@@ -747,14 +747,14 @@ label.label-bold {
fill: #fca326;
}
input::-moz-placeholder {
- color: #868686;
+ color: #89888d;
opacity: 1;
}
input::-ms-input-placeholder {
- color: #868686;
+ color: #89888d;
}
input:-ms-input-placeholder {
- color: #868686;
+ color: #89888d;
}
svg {
fill: currentColor;
@@ -805,7 +805,7 @@ svg {
}
.login-page .login-box,
.login-page .omniauth-container {
- box-shadow: 0 0 0 1px #dbdbdb;
+ box-shadow: 0 0 0 1px #dcdcde;
border-radius: 0.25rem;
}
.login-page .login-box .login-heading h3,
@@ -863,7 +863,7 @@ svg {
}
.login-page .new-session-tabs {
display: flex;
- box-shadow: 0 0 0 1px #dbdbdb;
+ box-shadow: 0 0 0 1px #dcdcde;
border-top-right-radius: 4px;
border-top-left-radius: 4px;
}
@@ -874,7 +874,7 @@ svg {
.login-page .new-session-tabs.nav-links-unboxed .nav-item {
border-left: 0;
border-right: 0;
- border-bottom: 1px solid #dbdbdb;
+ border-bottom: 1px solid #dcdcde;
background-color: transparent;
}
.login-page .new-session-tabs.custom-provider-tabs {
@@ -885,7 +885,7 @@ svg {
flex-basis: auto;
}
.login-page .new-session-tabs.custom-provider-tabs li:nth-child(n + 5) {
- border-top: 1px solid #dbdbdb;
+ border-top: 1px solid #dcdcde;
}
.login-page .new-session-tabs.custom-provider-tabs a {
font-size: 16px;
@@ -893,7 +893,7 @@ svg {
.login-page .new-session-tabs li {
flex: 1;
text-align: center;
- border-left: 1px solid #dbdbdb;
+ border-left: 1px solid #dcdcde;
}
.login-page .new-session-tabs li:first-of-type {
border-left: 0;
@@ -903,7 +903,7 @@ svg {
border-top-right-radius: 4px;
}
.login-page .new-session-tabs li:not(.active) {
- background-color: #fafafa;
+ background-color: #fbfafd;
}
.login-page .new-session-tabs li a {
width: 100%;
diff --git a/app/assets/stylesheets/themes/_dark.scss b/app/assets/stylesheets/themes/_dark.scss
index 7126c99988c..a3474d2ed50 100644
--- a/app/assets/stylesheets/themes/_dark.scss
+++ b/app/assets/stylesheets/themes/_dark.scss
@@ -1,15 +1,15 @@
-$gray-10: #1f1f1f;
-$gray-50: #303030;
-$gray-100: #404040;
-$gray-200: #525252;
-$gray-300: #5e5e5e;
-$gray-400: #868686;
-$gray-500: #999;
-$gray-600: #bfbfbf;
-$gray-700: #dbdbdb;
-$gray-800: #f0f0f0;
-$gray-900: #fafafa;
-$gray-950: #fff;
+$gray-10: #1f1e24;
+$gray-50: #333238;
+$gray-100: #434248;
+$gray-200: #535158;
+$gray-300: #626168;
+$gray-400: #737278;
+$gray-500: #89888d;
+$gray-600: #a4a3a8;
+$gray-700: #bfbfc3;
+$gray-800: #dcdcde;
+$gray-900: #ececef;
+$gray-950: #fbfafd;
$green-50: #0a4020;
$green-100: #0d532a;
diff --git a/app/controllers/jira_connect/application_controller.rb b/app/controllers/jira_connect/application_controller.rb
index a70c1ef4965..b9f0ea795e1 100644
--- a/app/controllers/jira_connect/application_controller.rb
+++ b/app/controllers/jira_connect/application_controller.rb
@@ -5,7 +5,6 @@ class JiraConnect::ApplicationController < ApplicationController
CORS_ALLOWED_METHODS = {
'/-/jira_connect/oauth_application_id' => %i[GET OPTIONS],
- '/-/jira_connect/subscriptions' => %i[GET POST OPTIONS],
'/-/jira_connect/subscriptions/*' => %i[DELETE OPTIONS]
}.freeze
diff --git a/app/graphql/types/metadata_type.rb b/app/graphql/types/metadata_type.rb
index b00fcfd38ad..492cca365f3 100644
--- a/app/graphql/types/metadata_type.rb
+++ b/app/graphql/types/metadata_type.rb
@@ -6,6 +6,8 @@ module Types
authorize :read_instance_metadata
+ field :enterprise, GraphQL::Types::Boolean, null: false,
+ description: 'Enterprise edition.'
field :kas, ::Types::Metadata::KasType, null: false,
description: 'Metadata about KAS.'
field :revision, GraphQL::Types::String, null: false,
diff --git a/app/models/active_session.rb b/app/models/active_session.rb
index dbc590668a8..b16c4a2b353 100644
--- a/app/models/active_session.rb
+++ b/app/models/active_session.rb
@@ -83,24 +83,26 @@ class ActiveSession
is_impersonated: request.session[:impersonator_id].present?
)
- redis.pipelined do |pipeline|
- pipeline.setex(
- key_name(user.id, session_private_id),
- expiry,
- active_user_session.dump
- )
-
- # Deprecated legacy format - temporary to support mixed deployments
- pipeline.setex(
- key_name_v1(user.id, session_private_id),
- expiry,
- Marshal.dump(active_user_session)
- )
-
- pipeline.sadd?(
- lookup_key_name(user.id),
- session_private_id
- )
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ redis.pipelined do |pipeline|
+ pipeline.setex(
+ key_name(user.id, session_private_id),
+ expiry,
+ active_user_session.dump
+ )
+
+ # Deprecated legacy format - temporary to support mixed deployments
+ pipeline.setex(
+ key_name_v1(user.id, session_private_id),
+ expiry,
+ Marshal.dump(active_user_session)
+ )
+
+ pipeline.sadd?(
+ lookup_key_name(user.id),
+ session_private_id
+ )
+ end
end
end
end
diff --git a/app/models/awareness_session.rb b/app/models/awareness_session.rb
index cca69e38b6f..0b652984630 100644
--- a/app/models/awareness_session.rb
+++ b/app/models/awareness_session.rb
@@ -63,16 +63,18 @@ class AwarenessSession # rubocop:disable Gitlab/NamespacedClass
user_key = user_sessions_key(user.id)
with_redis do |redis|
- redis.pipelined do |pipeline|
- pipeline.sadd?(user_key, id_i)
- pipeline.expire(user_key, USER_LIFETIME.to_i)
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ redis.pipelined do |pipeline|
+ pipeline.sadd?(user_key, id_i)
+ pipeline.expire(user_key, USER_LIFETIME.to_i)
- pipeline.zadd(users_key, timestamp.to_f, user.id)
+ pipeline.zadd(users_key, timestamp.to_f, user.id)
- # We also mark for expiry when a session key is created (first user joins),
- # because some users might never actively leave a session and the key could
- # therefore become stale, w/o us noticing.
- reset_session_expiry(pipeline)
+ # We also mark for expiry when a session key is created (first user joins),
+ # because some users might never actively leave a session and the key could
+ # therefore become stale, w/o us noticing.
+ reset_session_expiry(pipeline)
+ end
end
end
@@ -83,26 +85,33 @@ class AwarenessSession # rubocop:disable Gitlab/NamespacedClass
user_key = user_sessions_key(user.id)
with_redis do |redis|
- redis.pipelined do |pipeline|
- pipeline.srem?(user_key, id_i)
- pipeline.zrem(users_key, user.id)
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ redis.pipelined do |pipeline|
+ pipeline.srem?(user_key, id_i)
+ pipeline.zrem(users_key, user.id)
+ end
end
# cleanup orphan sessions and users
#
# this needs to be a second pipeline due to the delete operations being
# dependent on the result of the cardinality checks
- user_sessions_count, session_users_count = redis.pipelined do |pipeline|
- pipeline.scard(user_key)
- pipeline.zcard(users_key)
- end
+ user_sessions_count, session_users_count =
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ redis.pipelined do |pipeline|
+ pipeline.scard(user_key)
+ pipeline.zcard(users_key)
+ end
+ end
- redis.pipelined do |pipeline|
- pipeline.del(user_key) unless user_sessions_count > 0
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ redis.pipelined do |pipeline|
+ pipeline.del(user_key) unless user_sessions_count > 0
- unless session_users_count > 0
- pipeline.del(users_key)
- @id = nil
+ unless session_users_count > 0
+ pipeline.del(users_key)
+ @id = nil
+ end
end
end
end
diff --git a/app/models/incident_management/timeline_event_tag.rb b/app/models/incident_management/timeline_event_tag.rb
index 75d23f05e4e..d1e3fbc2a6a 100644
--- a/app/models/incident_management/timeline_event_tag.rb
+++ b/app/models/incident_management/timeline_event_tag.rb
@@ -20,7 +20,7 @@ module IncidentManagement
validates :name, uniqueness: { scope: :project_id, case_sensitive: false }
validates :name, length: { maximum: 255 }
- scope :by_names, -> (tag_names) { where(name: tag_names) }
+ scope :by_names, -> (tag_names) { where('lower(name) in (?)', tag_names.map(&:downcase)) }
def self.pluck_names
pluck(:name)
diff --git a/app/models/instance_metadata.rb b/app/models/instance_metadata.rb
index 6cac78178e0..47460c85671 100644
--- a/app/models/instance_metadata.rb
+++ b/app/models/instance_metadata.rb
@@ -1,11 +1,12 @@
# frozen_string_literal: true
class InstanceMetadata
- attr_reader :version, :revision, :kas
+ attr_reader :version, :revision, :kas, :enterprise
- def initialize(version: Gitlab::VERSION, revision: Gitlab.revision)
+ def initialize(version: Gitlab::VERSION, revision: Gitlab.revision, enterprise: Gitlab.ee?)
@version = version
@revision = revision
@kas = ::InstanceMetadata::Kas.new
+ @enterprise = enterprise
end
end
diff --git a/app/services/incident_management/timeline_events/create_service.rb b/app/services/incident_management/timeline_events/create_service.rb
index e8ec8aa6525..71ff5b64515 100644
--- a/app/services/incident_management/timeline_events/create_service.rb
+++ b/app/services/incident_management/timeline_events/create_service.rb
@@ -5,6 +5,7 @@ module IncidentManagement
DEFAULT_ACTION = 'comment'
DEFAULT_EDITABLE = false
DEFAULT_AUTO_CREATED = false
+ AUTOCREATE_TAGS = [TimelineEventTag::START_TIME_TAG_NAME, TimelineEventTag::END_TIME_TAG_NAME].freeze
class CreateService < TimelineEvents::BaseService
def initialize(incident, user, params)
@@ -94,6 +95,10 @@ module IncidentManagement
editable: params.fetch(:editable, DEFAULT_EDITABLE)
}
+ non_existing_tags = validate_tags(project, params[:timeline_event_tag_names])
+
+ return error("#{_("Following tags don't exist")}: #{non_existing_tags}") unless non_existing_tags.empty?
+
timeline_event = IncidentManagement::TimelineEvent.new(timeline_event_params)
if timeline_event.save(context: validation_context)
@@ -130,8 +135,11 @@ module IncidentManagement
end
def create_timeline_event_tag_links(timeline_event, tag_names)
- return unless params[:timeline_event_tag_names]
+ return unless tag_names&.any?
+ auto_create_predefined_tags(tag_names)
+
+ # Refetches the tag objects to consider predefined tags as well
tags = project.incident_management_timeline_event_tags.by_names(tag_names)
tag_links = tags.select(:id).map do |tag|
@@ -144,6 +152,30 @@ module IncidentManagement
IncidentManagement::TimelineEventTagLink.insert_all(tag_links) if tag_links.any?
end
+
+ def auto_create_predefined_tags(new_tags)
+ new_tags = new_tags.map(&:downcase)
+
+ tags_to_create = AUTOCREATE_TAGS.select { |tag| tag.downcase.in?(new_tags) }
+
+ tags_to_create.each do |name|
+ project.incident_management_timeline_event_tags.create(name: name)
+ end
+ end
+
+ def validate_tags(project, tag_names)
+ return [] unless tag_names&.any?
+
+ start_time_tag = AUTOCREATE_TAGS[0].downcase
+ end_time_tag = AUTOCREATE_TAGS[1].downcase
+
+ tag_names_downcased = tag_names.map(&:downcase)
+
+ tags = project.incident_management_timeline_event_tags.by_names(tag_names).pluck_names.map(&:downcase)
+
+ # remove tags from given tag_names and also remove predefined tags which can be auto created
+ tag_names_downcased - tags - [start_time_tag, end_time_tag]
+ end
end
end
end
diff --git a/app/validators/nested_attributes_duplicates_validator.rb b/app/validators/nested_attributes_duplicates_validator.rb
index b60350a6311..de219c300ba 100644
--- a/app/validators/nested_attributes_duplicates_validator.rb
+++ b/app/validators/nested_attributes_duplicates_validator.rb
@@ -25,11 +25,11 @@ class NestedAttributesDuplicatesValidator < ActiveModel::EachValidator
def validate_duplicates(record, attribute, values)
child_attributes.each do |child_attribute|
duplicates = values.reject(&:marked_for_destruction?).group_by(&:"#{child_attribute}").select { |_, v| v.many? }.map(&:first)
- if duplicates.any?
- error_message = +"have duplicate values (#{duplicates.join(", ")})"
- error_message << " for #{values.first.send(options[:scope])} scope" if options[:scope] # rubocop:disable GitlabSecurity/PublicSend
- record.errors.add(attribute, error_message)
- end
+ next unless duplicates.any?
+
+ error_message = +"have duplicate values (#{duplicates.join(", ")})"
+ error_message << " for #{values.first.send(options[:scope])} scope" if options[:scope] # rubocop:disable GitlabSecurity/PublicSend
+ record.errors.add(attribute, error_message)
end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/views/devise/confirmations/almost_there.haml b/app/views/devise/confirmations/almost_there.haml
index ef19ac33a15..01f9595f35c 100644
--- a/app/views/devise/confirmations/almost_there.haml
+++ b/app/views/devise/confirmations/almost_there.haml
@@ -1,4 +1,4 @@
-- user_email = "(#{params[:email]})" if params[:email].present?
+- user_email = "(#{params[:email]})" if Devise.email_regexp.match?(params[:email])
- request_link_start = '<a href="%{new_user_confirmation_path}">'.html_safe % { new_user_confirmation_path: new_user_confirmation_path }
- request_link_end = '</a>'.html_safe
- content_for :page_specific_javascripts do
diff --git a/config/feature_flags/development/board_grouped_by_epic_performance.yml b/config/feature_flags/development/board_grouped_by_epic_performance.yml
new file mode 100644
index 00000000000..08519f3c328
--- /dev/null
+++ b/config/feature_flags/development/board_grouped_by_epic_performance.yml
@@ -0,0 +1,8 @@
+---
+name: board_grouped_by_epic_performance
+introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/101640'
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381664
+milestone: '15.6'
+type: development
+group: group::product planning
+default_enabled: false
diff --git a/config/open_api.yml b/config/open_api.yml
index 7f02fd23484..ddf3d23dfc9 100644
--- a/config/open_api.yml
+++ b/config/open_api.yml
@@ -29,6 +29,8 @@ metadata:
description: Operations related to clusters
- name: container_registry
description: Operations related to container registry
+ - name: dashboard_annotations
+ description: Operations related to dashboard annotations
- name: dependency_proxy
description: Operations to manage dependency proxy for a groups
- name: deploy_keys
diff --git a/config/routes.rb b/config/routes.rb
index 5ebd7246e5a..27313854233 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -56,7 +56,6 @@ InitializerConnections.with_disabled_database_connections do
match '/oauth/revoke' => 'oauth/tokens#revoke', via: :options
match '/-/jira_connect/oauth_application_id' => 'jira_connect/cors_preflight_checks#index', via: :options
- match '/-/jira_connect/subscriptions' => 'jira_connect/cors_preflight_checks#index', via: :options
match '/-/jira_connect/subscriptions/:id' => 'jira_connect/cors_preflight_checks#index', via: :options
match '/-/jira_connect/installations' => 'jira_connect/cors_preflight_checks#index', via: :options
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index a8e48a952c0..b1f9d6ceae1 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -15783,6 +15783,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| Name | Type | Description |
| ---- | ---- | ----------- |
+| <a id="metadataenterprise"></a>`enterprise` | [`Boolean!`](#boolean) | Enterprise edition. |
| <a id="metadatakas"></a>`kas` | [`Kas!`](#kas) | Metadata about KAS. |
| <a id="metadatarevision"></a>`revision` | [`String!`](#string) | Revision. |
| <a id="metadataversion"></a>`version` | [`String!`](#string) | Version. |
diff --git a/doc/api/metadata.md b/doc/api/metadata.md
index 3803173b0b8..c3cbae70a54 100644
--- a/doc/api/metadata.md
+++ b/doc/api/metadata.md
@@ -24,6 +24,7 @@ Response body attributes:
| `kas.enabled` | boolean | Indicates whether KAS is enabled. |
| `kas.externalUrl` | string or null | URL used by the agents to communicate with KAS. It's `null` if `kas.enabled` is `false`. |
| `kas.version` | string or null | Version of KAS. It's `null` if `kas.enabled` is `false`. |
+| `enterprise` | boolean | Indicates whether GitLab instance is Enterprise Edition. |
Example request:
@@ -41,6 +42,7 @@ Example response:
"enabled": true,
"externalUrl": "grpc://gitlab.example.com:8150",
"version": "15.0.0"
- }
+ },
+ "enterprise": true
}
```
diff --git a/doc/api/suggestions.md b/doc/api/suggestions.md
index 0b6fa25c5c7..1e1f226481c 100644
--- a/doc/api/suggestions.md
+++ b/doc/api/suggestions.md
@@ -51,7 +51,7 @@ PUT /suggestions/batch_apply
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `ids` | integer | yes | The ID of a suggestion |
+| `ids` | integer | yes | The IDs of suggestions |
| `commit_message` | string | no | A custom commit message to use instead of the default generated message or the project's default message |
```shell
diff --git a/doc/development/pipelines/index.md b/doc/development/pipelines/index.md
index e29d38bb22f..01bb813e794 100644
--- a/doc/development/pipelines/index.md
+++ b/doc/development/pipelines/index.md
@@ -458,10 +458,13 @@ We also run our test suite against PG11 upon specific database library changes i
| `maintenance` scheduled pipelines for the `ruby3` branch (every odd-numbered hour), see below. | 12 (default version), 11 for DB library changes | 3.0 (coded in the branch) |
| `nightly` scheduled pipelines for the `master` branch | 12 (default version), 11, 13 | 2.7 (default version) |
-The pipeline configuration for the scheduled pipeline testing Ruby 3 is
-stored in the `ruby3-sync` branch. The pipeline updates the `ruby3` branch
-with latest `master`, and then it triggers a regular branch pipeline for
-`ruby3`. Any changes in `ruby3` are only for running the pipeline. It should
+There are 2 pipeline schedules used for testing Ruby 3. One is triggering a
+pipeline in `ruby3-sync` branch, which updates the `ruby3` branch with latest
+`master`, and no pipelines will be triggered by this push. The other schedule
+is triggering a pipeline in `ruby3` 5 minutes after it, which is considered
+the maintenance schedule to run test suites and update cache.
+
+Any changes in `ruby3` are only for running the pipeline. It should
never be merged back to `master`. Any other Ruby 3 changes should go into
`master` directly, which should be compatible with Ruby 2.7.
diff --git a/doc/user/product_analytics/index.md b/doc/user/product_analytics/index.md
new file mode 100644
index 00000000000..8e340fff32a
--- /dev/null
+++ b/doc/user/product_analytics/index.md
@@ -0,0 +1,48 @@
+---
+stage: Analyze
+group: Product Analytics
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Product analytics **(ULTIMATE)** **Alpha**
+
+> Introduced in GitLab 15.4 [with a flag](../../administration/feature_flags.md) named `cube_api_proxy`. Disabled by default.
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available per project or for your entire instance, ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `cube_api_proxy`.
+On GitLab.com, this feature is not available.
+This feature is not ready for production use.
+
+## Overview
+
+You can view the [product category](https://about.gitlab.com/direction/analytics/product-analytics/) page for more information about our direction. This page is a work in progress and will be updated as we add more features.
+
+## Product analytics dashboards
+
+Each project can define an unlimited number of dashboards. These dashboards are defined using our YAML schema and stored
+in the `.gitlab/product_analytics/dashboards/` directory. The name of the file is the name of the dashboard, and visualizations are shared across dashboards..
+
+Project maintainers can enforce approval rules on dashboard changes, and dashboards can be versioned in source control.
+
+### Define a dashboard
+
+To define a dashboard:
+
+1. In `.gitlab/product_analytics/dashboards/`, create a directory named like the dashboard. Each dashboard should have its own directory.
+1. In the new directory, create a `.yaml` file with the same name as the directory. This file contains the dashboard definition, and must conform to the JSON schema defined in `ee/app/validators/json_schemas/product_analytics_dashboard.json`.
+1. In the `.gitlab/product_analytics/dashboards/visualizations/` directory, create a `yaml` file. This file defines the visualization type for the dashboard, and must conform to the schema in
+`ee/app/validators/json_schemas/product_analytics_visualization.json`.
+
+The example below includes three dashboards and one visualization that applies to all dashboards.
+
+```plaintext
+.gitlab/product_analytics/dashboards
+├── conversion_funnels
+│ └── conversion_funnels.yaml
+├── demographic_breakdown
+│ └── demographic_breakdown.yaml
+├── north_star_metrics
+| └── north_star_metrics.yaml
+├── visualizations
+│ └── example_line_chart.yaml
+```
diff --git a/lib/api/api.rb b/lib/api/api.rb
index c0d5e84f5b1..94dfb7f598c 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -224,6 +224,7 @@ module API
mount ::API::MergeRequestApprovals
mount ::API::MergeRequestDiffs
mount ::API::Metadata
+ mount ::API::Metrics::Dashboard::Annotations
mount ::API::Metrics::UserStarredDashboards
mount ::API::PackageFiles
mount ::API::PersonalAccessTokens::SelfInformation
@@ -296,7 +297,6 @@ module API
mount ::API::MavenPackages
mount ::API::Members
mount ::API::MergeRequests
- mount ::API::Metrics::Dashboard::Annotations
mount ::API::Namespaces
mount ::API::Notes
mount ::API::NotificationSettings
diff --git a/lib/api/entities/metadata.rb b/lib/api/entities/metadata.rb
index 1e04b5c5982..7dfcad2ccab 100644
--- a/lib/api/entities/metadata.rb
+++ b/lib/api/entities/metadata.rb
@@ -10,6 +10,7 @@ module API
expose :externalUrl, documentation: { type: 'string', example: 'grpc://gitlab.example.com:8150' }
expose :version, documentation: { type: 'string', example: '15.0.0' }
end
+ expose :enterprise, documentation: { type: 'boolean' }
end
end
end
diff --git a/lib/api/entities/metrics/dashboard/annotation.rb b/lib/api/entities/metrics/dashboard/annotation.rb
index 66bd09d84f9..08d1a333259 100644
--- a/lib/api/entities/metrics/dashboard/annotation.rb
+++ b/lib/api/entities/metrics/dashboard/annotation.rb
@@ -5,13 +5,13 @@ module API
module Metrics
module Dashboard
class Annotation < Grape::Entity
- expose :id
- expose :starting_at
- expose :ending_at
- expose :dashboard_path
- expose :description
- expose :environment_id
- expose :cluster_id
+ expose :id, documentation: { type: 'integer', example: 4 }
+ expose :starting_at, documentation: { type: 'dateTime', example: '2016-04-08T03:45:40.000Z' }
+ expose :ending_at, documentation: { type: 'dateTime', example: '2016-08-08T09:00:00.000Z' }
+ expose :dashboard_path, documentation: { type: 'string', example: '.gitlab/dashboards/custom_metrics.yml' }
+ expose :description, documentation: { type: 'string', example: 'annotation description' }
+ expose :environment_id, documentation: { type: 'integer', example: 1 }
+ expose :cluster_id, documentation: { type: 'integer', example: 2 }
end
end
end
diff --git a/lib/api/metadata.rb b/lib/api/metadata.rb
index 2fdb97f98ef..788d9843c63 100644
--- a/lib/api/metadata.rb
+++ b/lib/api/metadata.rb
@@ -23,6 +23,7 @@ module API
externalUrl
version
}
+ enterprise
}
}
EOF
diff --git a/lib/api/metrics/dashboard/annotations.rb b/lib/api/metrics/dashboard/annotations.rb
index 478adcdce70..6ba154191be 100644
--- a/lib/api/metrics/dashboard/annotations.rb
+++ b/lib/api/metrics/dashboard/annotations.rb
@@ -7,8 +7,15 @@ module API
feature_category :metrics
urgency :low
- desc 'Create a new monitoring dashboard annotation' do
+ desc 'Create a new annotation' do
+ detail 'Creates a new monitoring dashboard annotation'
success Entities::Metrics::Dashboard::Annotation
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[dashboard_annotations]
end
ANNOTATIONS_SOURCES = [
@@ -20,12 +27,16 @@ module API
resource annotations_source[:resource] do
params do
requires :starting_at, type: DateTime,
- desc: 'Date time indicating starting moment to which the annotation relates.'
+ desc: 'Date time string, ISO 8601 formatted, such as 2016-03-11T03:45:40Z.'\
+ 'Timestamp marking start point of annotation.'
optional :ending_at, type: DateTime,
- desc: 'Date time indicating ending moment to which the annotation relates.'
+ desc: 'Date time string, ISO 8601 formatted, such as 2016-03-11T03:45:40Z.'\
+ 'Timestamp marking end point of annotation.'\
+ 'When not supplied, an annotation displays as a single event at the start point.'
requires :dashboard_path, type: String, coerce_with: -> (val) { CGI.unescape(val) },
- desc: 'The path to a file defining the dashboard on which the annotation should be added'
- requires :description, type: String, desc: 'The description of the annotation'
+ desc: 'ID of the dashboard which needs to be annotated.'\
+ 'Treated as a CGI-escaped path, and automatically un-escaped.'
+ requires :description, type: String, desc: 'Description of the annotation.'
end
post ':id/metrics_dashboard/annotations' do
@@ -33,7 +44,9 @@ module API
forbidden! unless can?(current_user, :create_metrics_dashboard_annotation, annotations_source_object)
- create_service_params = declared(params).merge(annotations_source[:create_service_param_key] => annotations_source_object)
+ create_service_params = declared(params).merge(
+ annotations_source[:create_service_param_key] => annotations_source_object
+ )
result = ::Metrics::Dashboard::Annotations::CreateService.new(current_user, create_service_params).execute
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index 902eb8f6659..a8b3c12a2a2 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -396,13 +396,13 @@ module Backup
timestamp = matched[1].to_i
- if Time.at(timestamp) < (Time.now - keep_time)
- begin
- FileUtils.rm(file)
- removed += 1
- rescue StandardError => e
- puts_time "Deleting #{file} failed: #{e.message}".color(:red)
- end
+ next unless Time.at(timestamp) < (Time.now - keep_time)
+
+ begin
+ FileUtils.rm(file)
+ removed += 1
+ rescue StandardError => e
+ puts_time "Deleting #{file} failed: #{e.message}".color(:red)
end
end
end
diff --git a/lib/banzai/filter/external_link_filter.rb b/lib/banzai/filter/external_link_filter.rb
index d1a0f8e5859..0a76c84efe5 100644
--- a/lib/banzai/filter/external_link_filter.rb
+++ b/lib/banzai/filter/external_link_filter.rb
@@ -22,12 +22,12 @@ module Banzai
addressable_uri = nil
end
- unless internal_url?(addressable_uri)
- punycode_autolink_node!(addressable_uri, node)
- sanitize_link_text!(node)
- add_malicious_tooltip!(addressable_uri, node)
- add_nofollow!(addressable_uri, node)
- end
+ next if internal_url?(addressable_uri)
+
+ punycode_autolink_node!(addressable_uri, node)
+ sanitize_link_text!(node)
+ add_malicious_tooltip!(addressable_uri, node)
+ add_nofollow!(addressable_uri, node)
end
doc
diff --git a/lib/banzai/filter/footnote_filter.rb b/lib/banzai/filter/footnote_filter.rb
index f5c4b788ad8..f10efdccdf1 100644
--- a/lib/banzai/filter/footnote_filter.rb
+++ b/lib/banzai/filter/footnote_filter.rb
@@ -44,25 +44,25 @@ module Banzai
node_xpath = Gitlab::Utils::Nokogiri.css_to_xpath(css)
footnote_node = doc.at_xpath(node_xpath)
- if footnote_node || modified_footnotes[ref_num]
- link_node[:href] += rand_suffix
- link_node[:id] += rand_suffix
+ next unless footnote_node || modified_footnotes[ref_num]
- # Sanitization stripped off class - add it back in
- link_node.parent.append_class('footnote-ref')
+ link_node[:href] += rand_suffix
+ link_node[:id] += rand_suffix
- unless modified_footnotes[ref_num]
- footnote_node[:id] += rand_suffix
- backref_node = footnote_node.at_css("a[href=\"##{fnref_id(ref_num)}\"]")
+ # Sanitization stripped off class - add it back in
+ link_node.parent.append_class('footnote-ref')
- if backref_node
- backref_node[:href] += rand_suffix
- backref_node.append_class('footnote-backref')
- end
+ next if modified_footnotes[ref_num]
- modified_footnotes[ref_num] = true
- end
+ footnote_node[:id] += rand_suffix
+ backref_node = footnote_node.at_css("a[href=\"##{fnref_id(ref_num)}\"]")
+
+ if backref_node
+ backref_node[:href] += rand_suffix
+ backref_node.append_class('footnote-backref')
end
+
+ modified_footnotes[ref_num] = true
end
doc
diff --git a/lib/banzai/filter/kroki_filter.rb b/lib/banzai/filter/kroki_filter.rb
index 0ce70843675..26f42c6b194 100644
--- a/lib/banzai/filter/kroki_filter.rb
+++ b/lib/banzai/filter/kroki_filter.rb
@@ -32,16 +32,16 @@ module Banzai
img_tag = Nokogiri::HTML::DocumentFragment.parse(%(<img src="#{image_src}" />))
img_tag = img_tag.children.first
- unless img_tag.nil?
- lazy_load = diagram_src.length > MAX_CHARACTER_LIMIT
- img_tag.set_attribute('hidden', '') if lazy_load
- img_tag.set_attribute('class', 'js-render-kroki')
+ next if img_tag.nil?
- img_tag.set_attribute('data-diagram', diagram_type)
- img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(diagram_src)}")
+ lazy_load = diagram_src.length > MAX_CHARACTER_LIMIT
+ img_tag.set_attribute('hidden', '') if lazy_load
+ img_tag.set_attribute('class', 'js-render-kroki')
- node.parent.replace(img_tag)
- end
+ img_tag.set_attribute('data-diagram', diagram_type)
+ img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(diagram_src)}")
+
+ node.parent.replace(img_tag)
end
doc
diff --git a/lib/banzai/filter/math_filter.rb b/lib/banzai/filter/math_filter.rb
index ac009008040..1d854d6599b 100644
--- a/lib/banzai/filter/math_filter.rb
+++ b/lib/banzai/filter/math_filter.rb
@@ -107,19 +107,18 @@ module Banzai
# We need a sibling before and after.
# They should end and start with $ respectively.
- if closing && opening &&
- closing.text? && opening.text? &&
- closing.content.first == DOLLAR_SIGN &&
- opening.content.last == DOLLAR_SIGN
-
- code[:class] = MATH_CLASSES
- code[STYLE_ATTRIBUTE] = 'inline'
- closing.content = closing.content[1..]
- opening.content = opening.content[0..-2]
-
- @nodes_count += 1
- break if @nodes_count >= RENDER_NODES_LIMIT
- end
+ next unless closing && opening &&
+ closing.text? && opening.text? &&
+ closing.content.first == DOLLAR_SIGN &&
+ opening.content.last == DOLLAR_SIGN
+
+ code[:class] = MATH_CLASSES
+ code[STYLE_ATTRIBUTE] = 'inline'
+ closing.content = closing.content[1..]
+ opening.content = opening.content[0..-2]
+
+ @nodes_count += 1
+ break if @nodes_count >= RENDER_NODES_LIMIT
end
end
diff --git a/lib/banzai/filter/plantuml_filter.rb b/lib/banzai/filter/plantuml_filter.rb
index 82f6247cf03..6a1fa64fb76 100644
--- a/lib/banzai/filter/plantuml_filter.rb
+++ b/lib/banzai/filter/plantuml_filter.rb
@@ -17,12 +17,12 @@ module Banzai
img_tag = Nokogiri::HTML::DocumentFragment.parse(
Asciidoctor::PlantUml::Processor.plantuml_content(node.content, {})).css('img').first
- unless img_tag.nil?
- img_tag.set_attribute('data-diagram', 'plantuml')
- img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(node.content)}")
+ next if img_tag.nil?
- node.parent.replace(img_tag)
- end
+ img_tag.set_attribute('data-diagram', 'plantuml')
+ img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(node.content)}")
+
+ node.parent.replace(img_tag)
end
doc
diff --git a/lib/banzai/filter/table_of_contents_filter.rb b/lib/banzai/filter/table_of_contents_filter.rb
index 1c794a81d9d..d76009d08e1 100644
--- a/lib/banzai/filter/table_of_contents_filter.rb
+++ b/lib/banzai/filter/table_of_contents_filter.rb
@@ -33,17 +33,17 @@ module Banzai
header_root = current_header = HeaderNode.new
doc.xpath(XPATH).each do |node|
- if header_content = node.children.first
- id = string_to_anchor(node.text[0...255])
+ next unless header_content = node.children.first
- uniq = headers[id] > 0 ? "-#{headers[id]}" : ''
- headers[id] += 1
- href = "#{id}#{uniq}"
+ id = string_to_anchor(node.text[0...255])
- current_header = HeaderNode.new(node: node, href: href, previous_header: current_header)
+ uniq = headers[id] > 0 ? "-#{headers[id]}" : ''
+ headers[id] += 1
+ href = "#{id}#{uniq}"
- header_content.add_previous_sibling(anchor_tag(href))
- end
+ current_header = HeaderNode.new(node: node, href: href, previous_header: current_header)
+
+ header_content.add_previous_sibling(anchor_tag(href))
end
push_toc(header_root.children, root: true)
diff --git a/lib/gitlab/background_migration/encrypt_static_object_token.rb b/lib/gitlab/background_migration/encrypt_static_object_token.rb
index e1805d40bab..961dea028c9 100644
--- a/lib/gitlab/background_migration/encrypt_static_object_token.rb
+++ b/lib/gitlab/background_migration/encrypt_static_object_token.rb
@@ -40,8 +40,9 @@ module Gitlab
encrypted_tokens_sql = user_encrypted_tokens.compact.map { |(id, token)| "(#{id}, '#{token}')" }.join(',')
- if user_encrypted_tokens.present?
- User.connection.execute(<<~SQL)
+ next unless user_encrypted_tokens.present?
+
+ User.connection.execute(<<~SQL)
WITH cte(cte_id, cte_token) AS #{::Gitlab::Database::AsWithMaterialized.materialized_if_supported} (
SELECT *
FROM (VALUES #{encrypted_tokens_sql}) AS t (id, token)
@@ -50,8 +51,7 @@ module Gitlab
SET static_object_token_encrypted = cte_token
FROM cte
WHERE cte_id = id
- SQL
- end
+ SQL
end
mark_job_as_succeeded(start_id, end_id)
diff --git a/lib/gitlab/cache/import/caching.rb b/lib/gitlab/cache/import/caching.rb
index 1646c9fbfef..7fec6584ba3 100644
--- a/lib/gitlab/cache/import/caching.rb
+++ b/lib/gitlab/cache/import/caching.rb
@@ -161,13 +161,15 @@ module Gitlab
# timeout - The time after which the cache key should expire.
def self.write_multiple(mapping, key_prefix: nil, timeout: TIMEOUT)
with_redis do |redis|
- redis.pipelined do |multi|
- mapping.each do |raw_key, value|
- key = cache_key_for("#{key_prefix}#{raw_key}")
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ redis.pipelined do |multi|
+ mapping.each do |raw_key, value|
+ key = cache_key_for("#{key_prefix}#{raw_key}")
- validate_redis_value!(value)
+ validate_redis_value!(value)
- multi.set(key, value, ex: timeout)
+ multi.set(key, value, ex: timeout)
+ end
end
end
end
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 2456a5dd68e..04cf056199c 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -217,13 +217,13 @@ module Gitlab
Rails.application.config.paths['db'].each do |db_path|
path = Rails.root.join(db_path, 'post_migrate').to_s
- unless Rails.application.config.paths['db/migrate'].include? path
- Rails.application.config.paths['db/migrate'] << path
+ next if Rails.application.config.paths['db/migrate'].include? path
- # Rails memoizes migrations at certain points where it won't read the above
- # path just yet. As such we must also update the following list of paths.
- ActiveRecord::Migrator.migrations_paths << path
- end
+ Rails.application.config.paths['db/migrate'] << path
+
+ # Rails memoizes migrations at certain points where it won't read the above
+ # path just yet. As such we must also update the following list of paths.
+ ActiveRecord::Migrator.migrations_paths << path
end
end
diff --git a/lib/gitlab/discussions_diff/highlight_cache.rb b/lib/gitlab/discussions_diff/highlight_cache.rb
index 62f7f268f07..14cb773251b 100644
--- a/lib/gitlab/discussions_diff/highlight_cache.rb
+++ b/lib/gitlab/discussions_diff/highlight_cache.rb
@@ -15,11 +15,13 @@ module Gitlab
# mapping - Write multiple cache values at once
def write_multiple(mapping)
with_redis do |redis|
- redis.multi do |multi|
- mapping.each do |raw_key, value|
- key = cache_key_for(raw_key)
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ redis.multi do |multi|
+ mapping.each do |raw_key, value|
+ key = cache_key_for(raw_key)
- multi.set(key, gzip_compress(value.to_json), ex: EXPIRATION)
+ multi.set(key, gzip_compress(value.to_json), ex: EXPIRATION)
+ end
end
end
end
diff --git a/lib/gitlab/etag_caching/store.rb b/lib/gitlab/etag_caching/store.rb
index 437d577e70e..bc97c88ce85 100644
--- a/lib/gitlab/etag_caching/store.rb
+++ b/lib/gitlab/etag_caching/store.rb
@@ -15,10 +15,12 @@ module Gitlab
def touch(*keys, only_if_missing: false)
etags = keys.map { generate_etag }
- Gitlab::Redis::SharedState.with do |redis|
- 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)
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ Gitlab::Redis::SharedState.with do |redis|
+ 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
end
end
end
diff --git a/lib/gitlab/gitaly_client/blob_service.rb b/lib/gitlab/gitaly_client/blob_service.rb
index 3b08a833aeb..6d87c3329d7 100644
--- a/lib/gitlab/gitaly_client/blob_service.rb
+++ b/lib/gitlab/gitaly_client/blob_service.rb
@@ -4,9 +4,12 @@ module Gitlab
module GitalyClient
class BlobService
include Gitlab::EncodingHelper
+ include WithFeatureFlagActors
def initialize(repository)
@gitaly_repo = repository.gitaly_repository
+
+ self.repository_actor = repository
end
def get_blob(oid:, limit:)
@@ -15,7 +18,7 @@ module Gitlab
oid: oid,
limit: limit
)
- response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_blob, request, timeout: GitalyClient.fast_timeout)
+ response = gitaly_client_call(@gitaly_repo.storage_name, :blob_service, :get_blob, request, timeout: GitalyClient.fast_timeout)
consume_blob_response(response)
end
@@ -35,7 +38,7 @@ module Gitlab
GitalyClient.medium_timeout
end
- response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :list_blobs, request, timeout: timeout)
+ response = gitaly_client_call(@gitaly_repo.storage_name, :blob_service, :list_blobs, request, timeout: timeout)
GitalyClient::BlobsStitcher.new(GitalyClient::ListBlobsAdapter.new(response))
end
@@ -47,7 +50,7 @@ module Gitlab
blob_ids: blob_ids
)
- response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_lfs_pointers, request, timeout: GitalyClient.medium_timeout)
+ response = gitaly_client_call(@gitaly_repo.storage_name, :blob_service, :get_lfs_pointers, request, timeout: GitalyClient.medium_timeout)
map_lfs_pointers(response)
end
@@ -64,7 +67,7 @@ module Gitlab
limit: limit
)
- response = GitalyClient.call(
+ response = gitaly_client_call(
@gitaly_repo.storage_name,
:blob_service,
:get_blobs,
@@ -87,7 +90,7 @@ module Gitlab
limit: limit
)
- response = GitalyClient.call(
+ response = gitaly_client_call(
@gitaly_repo.storage_name,
:blob_service,
:get_blobs,
@@ -107,7 +110,7 @@ module Gitlab
GitalyClient.medium_timeout
end
- response = GitalyClient.call(
+ response = gitaly_client_call(
@gitaly_repo.storage_name,
:blob_service,
rpc,
@@ -123,7 +126,7 @@ module Gitlab
revisions: [encode_binary("--all")]
)
- response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :list_lfs_pointers, request, timeout: GitalyClient.medium_timeout)
+ response = gitaly_client_call(@gitaly_repo.storage_name, :blob_service, :list_lfs_pointers, request, timeout: GitalyClient.medium_timeout)
map_lfs_pointers(response)
end
diff --git a/lib/gitlab/gitaly_client/cleanup_service.rb b/lib/gitlab/gitaly_client/cleanup_service.rb
index 649aaa46362..3c2c41a244e 100644
--- a/lib/gitlab/gitaly_client/cleanup_service.rb
+++ b/lib/gitlab/gitaly_client/cleanup_service.rb
@@ -3,6 +3,8 @@
module Gitlab
module GitalyClient
class CleanupService
+ include WithFeatureFlagActors
+
attr_reader :repository, :gitaly_repo, :storage
# 'repository' is a Gitlab::Git::Repository
@@ -10,10 +12,12 @@ module Gitlab
@repository = repository
@gitaly_repo = repository.gitaly_repository
@storage = repository.storage
+
+ self.repository_actor = repository
end
def apply_bfg_object_map_stream(io, &blk)
- response = GitalyClient.call(
+ response = gitaly_client_call(
storage,
:cleanup_service,
:apply_bfg_object_map_stream,
diff --git a/lib/gitlab/gitaly_client/conflicts_service.rb b/lib/gitlab/gitaly_client/conflicts_service.rb
index 982454b117e..38f648ccc31 100644
--- a/lib/gitlab/gitaly_client/conflicts_service.rb
+++ b/lib/gitlab/gitaly_client/conflicts_service.rb
@@ -4,6 +4,7 @@ module Gitlab
module GitalyClient
class ConflictsService
include Gitlab::EncodingHelper
+ include WithFeatureFlagActors
MAX_MSG_SIZE = 128.kilobytes.freeze
@@ -12,6 +13,8 @@ module Gitlab
@repository = repository
@our_commit_oid = our_commit_oid
@their_commit_oid = their_commit_oid
+
+ self.repository_actor = repository
end
def list_conflict_files(allow_tree_conflicts: false)
@@ -21,7 +24,7 @@ module Gitlab
their_commit_oid: @their_commit_oid,
allow_tree_conflicts: allow_tree_conflicts
)
- response = GitalyClient.call(@repository.storage, :conflicts_service, :list_conflict_files, request, timeout: GitalyClient.long_timeout)
+ response = gitaly_client_call(@repository.storage, :conflicts_service, :list_conflict_files, request, timeout: GitalyClient.long_timeout)
GitalyClient::ConflictFilesStitcher.new(response, @gitaly_repo)
end
@@ -50,7 +53,7 @@ module Gitlab
end
end
- response = GitalyClient.call(@repository.storage, :conflicts_service, :resolve_conflicts, req_enum, remote_storage: target_repository.storage, timeout: GitalyClient.long_timeout)
+ response = gitaly_client_call(@repository.storage, :conflicts_service, :resolve_conflicts, req_enum, remote_storage: target_repository.storage, timeout: GitalyClient.long_timeout)
if response.resolution_error.present?
raise Gitlab::Git::Conflict::Resolver::ResolutionError, response.resolution_error
diff --git a/lib/gitlab/gitaly_client/object_pool_service.rb b/lib/gitlab/gitaly_client/object_pool_service.rb
index 786ef0ebebe..e07bf3fbccc 100644
--- a/lib/gitlab/gitaly_client/object_pool_service.rb
+++ b/lib/gitlab/gitaly_client/object_pool_service.rb
@@ -3,6 +3,8 @@
module Gitlab
module GitalyClient
class ObjectPoolService
+ include WithFeatureFlagActors
+
attr_reader :object_pool, :storage
def initialize(object_pool)
@@ -15,8 +17,10 @@ module Gitlab
object_pool: object_pool,
origin: repository.gitaly_repository)
- GitalyClient.call(storage, :object_pool_service, :create_object_pool,
- request, timeout: GitalyClient.medium_timeout)
+ GitalyClient.with_feature_flag_actors(**gitaly_feature_flag_actors(repository)) do
+ GitalyClient.call(storage, :object_pool_service, :create_object_pool,
+ request, timeout: GitalyClient.medium_timeout)
+ end
end
def delete
@@ -32,8 +36,10 @@ module Gitlab
repository: repository.gitaly_repository
)
- GitalyClient.call(storage, :object_pool_service, :link_repository_to_object_pool,
- request, timeout: GitalyClient.fast_timeout)
+ GitalyClient.with_feature_flag_actors(**gitaly_feature_flag_actors(repository)) do
+ GitalyClient.call(storage, :object_pool_service, :link_repository_to_object_pool,
+ request, timeout: GitalyClient.fast_timeout)
+ end
end
def fetch(repository)
@@ -42,8 +48,10 @@ module Gitlab
origin: repository.gitaly_repository
)
- GitalyClient.call(storage, :object_pool_service, :fetch_into_object_pool,
- request, timeout: GitalyClient.long_timeout)
+ GitalyClient.with_feature_flag_actors(**gitaly_feature_flag_actors(repository)) do
+ GitalyClient.call(storage, :object_pool_service, :fetch_into_object_pool,
+ request, timeout: GitalyClient.long_timeout)
+ end
end
end
end
diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb
index 298162a5e2c..2312def5efc 100644
--- a/lib/gitlab/gitaly_client/operation_service.rb
+++ b/lib/gitlab/gitaly_client/operation_service.rb
@@ -4,12 +4,15 @@ module Gitlab
module GitalyClient
class OperationService
include Gitlab::EncodingHelper
+ include WithFeatureFlagActors
MAX_MSG_SIZE = 128.kilobytes.freeze
def initialize(repository)
@gitaly_repo = repository.gitaly_repository
@repository = repository
+
+ self.repository_actor = repository
end
def rm_tag(tag_name, user)
@@ -19,7 +22,7 @@ module Gitlab
user: Gitlab::Git::User.from_gitlab(user).to_gitaly
)
- response = GitalyClient.call(@repository.storage, :operation_service, :user_delete_tag, request, timeout: GitalyClient.long_timeout)
+ response = gitaly_client_call(@repository.storage, :operation_service, :user_delete_tag, request, timeout: GitalyClient.long_timeout)
if pre_receive_error = response.pre_receive_error.presence
raise Gitlab::Git::PreReceiveError, pre_receive_error
@@ -36,7 +39,7 @@ module Gitlab
timestamp: Google::Protobuf::Timestamp.new(seconds: Time.now.utc.to_i)
)
- response = GitalyClient.call(@repository.storage, :operation_service, :user_create_tag, request, timeout: GitalyClient.long_timeout)
+ response = gitaly_client_call(@repository.storage, :operation_service, :user_create_tag, request, timeout: GitalyClient.long_timeout)
if pre_receive_error = response.pre_receive_error.presence
raise Gitlab::Git::PreReceiveError, pre_receive_error
elsif response.exists
@@ -73,7 +76,7 @@ module Gitlab
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
start_point: encode_binary(start_point)
)
- response = GitalyClient.call(@repository.storage, :operation_service,
+ response = gitaly_client_call(@repository.storage, :operation_service,
:user_create_branch, request, timeout: GitalyClient.long_timeout)
if response.pre_receive_error.present?
@@ -110,7 +113,7 @@ module Gitlab
oldrev: encode_binary(oldrev)
)
- response = GitalyClient.call(@repository.storage, :operation_service,
+ response = gitaly_client_call(@repository.storage, :operation_service,
:user_update_branch, request, timeout: GitalyClient.long_timeout)
if pre_receive_error = response.pre_receive_error.presence
@@ -125,7 +128,7 @@ module Gitlab
user: Gitlab::Git::User.from_gitlab(user).to_gitaly
)
- response = GitalyClient.call(@repository.storage, :operation_service,
+ response = gitaly_client_call(@repository.storage, :operation_service,
:user_delete_branch, request, timeout: GitalyClient.long_timeout)
if pre_receive_error = response.pre_receive_error.presence
@@ -156,7 +159,7 @@ module Gitlab
timestamp: Google::Protobuf::Timestamp.new(seconds: Time.now.utc.to_i)
)
- response = GitalyClient.call(@repository.storage, :operation_service,
+ response = gitaly_client_call(@repository.storage, :operation_service,
:user_merge_to_ref, request, timeout: GitalyClient.long_timeout)
response.commit_id
@@ -164,7 +167,7 @@ module Gitlab
def user_merge_branch(user, source_sha, target_branch, message)
request_enum = QueueEnumerator.new
- response_enum = GitalyClient.call(
+ response_enum = gitaly_client_call(
@repository.storage,
:operation_service,
:user_merge_branch,
@@ -225,7 +228,7 @@ module Gitlab
branch: encode_binary(target_branch)
)
- response = GitalyClient.call(
+ response = gitaly_client_call(
@repository.storage,
:operation_service,
:user_ff_branch,
@@ -268,7 +271,7 @@ module Gitlab
request_enum = QueueEnumerator.new
rebase_sha = nil
- response_enum = GitalyClient.call(
+ response_enum = gitaly_client_call(
@repository.storage,
:operation_service,
:user_rebase_confirmable,
@@ -334,7 +337,7 @@ module Gitlab
timestamp: Google::Protobuf::Timestamp.new(seconds: time.to_i)
)
- response = GitalyClient.call(
+ response = gitaly_client_call(
@repository.storage,
:operation_service,
:user_squash,
@@ -376,7 +379,7 @@ module Gitlab
timestamp: Google::Protobuf::Timestamp.new(seconds: Time.now.utc.to_i)
)
- response = GitalyClient.call(
+ response = gitaly_client_call(
@repository.storage,
:operation_service,
:user_update_submodule,
@@ -422,7 +425,7 @@ module Gitlab
end
end
- response = GitalyClient.call(
+ response = gitaly_client_call(
@repository.storage, :operation_service, :user_commit_files, req_enum,
timeout: GitalyClient.long_timeout, remote_storage: start_repository&.storage)
@@ -473,7 +476,7 @@ module Gitlab
end
end
- response = GitalyClient.call(@repository.storage, :operation_service,
+ response = gitaly_client_call(@repository.storage, :operation_service,
:user_apply_patch, chunks, timeout: GitalyClient.long_timeout)
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
@@ -509,7 +512,7 @@ module Gitlab
dry_run: dry_run
)
- response = GitalyClient.call(
+ response = gitaly_client_call(
@repository.storage,
:operation_service,
:"user_#{rpc}",
diff --git a/lib/gitlab/gitaly_client/praefect_info_service.rb b/lib/gitlab/gitaly_client/praefect_info_service.rb
index 127f8cfbdf6..b565898acf8 100644
--- a/lib/gitlab/gitaly_client/praefect_info_service.rb
+++ b/lib/gitlab/gitaly_client/praefect_info_service.rb
@@ -3,16 +3,20 @@
module Gitlab
module GitalyClient
class PraefectInfoService
+ include WithFeatureFlagActors
+
def initialize(repository)
@repository = repository
@gitaly_repo = repository.gitaly_repository
@storage = repository.storage
+
+ self.repository_actor = repository
end
def replicas
request = Gitaly::RepositoryReplicasRequest.new(repository: @gitaly_repo)
- GitalyClient.call(@storage, :praefect_info_service, :repository_replicas, request, timeout: GitalyClient.fast_timeout)
+ gitaly_client_call(@storage, :praefect_info_service, :repository_replicas, request, timeout: GitalyClient.fast_timeout)
end
end
end
diff --git a/lib/gitlab/gitaly_client/ref_service.rb b/lib/gitlab/gitaly_client/ref_service.rb
index bd6ff30df98..de76ade76cb 100644
--- a/lib/gitlab/gitaly_client/ref_service.rb
+++ b/lib/gitlab/gitaly_client/ref_service.rb
@@ -4,6 +4,7 @@ module Gitlab
module GitalyClient
class RefService
include Gitlab::EncodingHelper
+ include WithFeatureFlagActors
TAGS_SORT_KEY = {
'name' => Gitaly::FindAllTagsRequest::SortBy::Key::REFNAME,
@@ -21,17 +22,19 @@ module Gitlab
@repository = repository
@gitaly_repo = repository.gitaly_repository
@storage = repository.storage
+
+ self.repository_actor = repository
end
def branches
request = Gitaly::FindAllBranchesRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@storage, :ref_service, :find_all_branches, request, timeout: GitalyClient.fast_timeout)
+ response = gitaly_client_call(@storage, :ref_service, :find_all_branches, request, timeout: GitalyClient.fast_timeout)
consume_find_all_branches_response(response)
end
def remote_branches(remote_name)
request = Gitaly::FindAllRemoteBranchesRequest.new(repository: @gitaly_repo, remote_name: remote_name)
- response = GitalyClient.call(@storage, :ref_service, :find_all_remote_branches, request, timeout: GitalyClient.medium_timeout)
+ response = gitaly_client_call(@storage, :ref_service, :find_all_remote_branches, request, timeout: GitalyClient.medium_timeout)
consume_find_all_remote_branches_response(remote_name, response)
end
@@ -41,25 +44,25 @@ module Gitlab
merged_only: true,
merged_branches: branch_names.map { |s| encode_binary(s) }
)
- response = GitalyClient.call(@storage, :ref_service, :find_all_branches, request, timeout: GitalyClient.fast_timeout)
+ response = gitaly_client_call(@storage, :ref_service, :find_all_branches, request, timeout: GitalyClient.fast_timeout)
consume_find_all_branches_response(response)
end
def default_branch_name
request = Gitaly::FindDefaultBranchNameRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@storage, :ref_service, :find_default_branch_name, request, timeout: GitalyClient.fast_timeout)
+ response = gitaly_client_call(@storage, :ref_service, :find_default_branch_name, request, timeout: GitalyClient.fast_timeout)
Gitlab::Git.branch_name(response.name)
end
def branch_names
request = Gitaly::FindAllBranchNamesRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@storage, :ref_service, :find_all_branch_names, request, timeout: GitalyClient.fast_timeout)
+ response = gitaly_client_call(@storage, :ref_service, :find_all_branch_names, request, timeout: GitalyClient.fast_timeout)
consume_refs_response(response) { |name| Gitlab::Git.branch_name(name) }
end
def tag_names
request = Gitaly::FindAllTagNamesRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@storage, :ref_service, :find_all_tag_names, request, timeout: GitalyClient.fast_timeout)
+ response = gitaly_client_call(@storage, :ref_service, :find_all_tag_names, request, timeout: GitalyClient.fast_timeout)
consume_refs_response(response) { |name| Gitlab::Git.tag_name(name) }
end
@@ -74,7 +77,7 @@ module Gitlab
def local_branches(sort_by: nil, pagination_params: nil)
request = Gitaly::FindLocalBranchesRequest.new(repository: @gitaly_repo, pagination_params: pagination_params)
request.sort_by = sort_local_branches_by_param(sort_by) if sort_by
- response = GitalyClient.call(@storage, :ref_service, :find_local_branches, request, timeout: GitalyClient.fast_timeout)
+ response = gitaly_client_call(@storage, :ref_service, :find_local_branches, request, timeout: GitalyClient.fast_timeout)
consume_find_local_branches_response(response)
end
@@ -82,13 +85,13 @@ module Gitlab
request = Gitaly::FindAllTagsRequest.new(repository: @gitaly_repo, pagination_params: pagination_params)
request.sort_by = sort_tags_by_param(sort_by) if sort_by
- response = GitalyClient.call(@storage, :ref_service, :find_all_tags, request, timeout: GitalyClient.medium_timeout)
+ response = gitaly_client_call(@storage, :ref_service, :find_all_tags, request, timeout: GitalyClient.medium_timeout)
consume_tags_response(response)
end
def ref_exists?(ref_name)
request = Gitaly::RefExistsRequest.new(repository: @gitaly_repo, ref: encode_binary(ref_name))
- response = GitalyClient.call(@storage, :ref_service, :ref_exists, request, timeout: GitalyClient.fast_timeout)
+ response = gitaly_client_call(@storage, :ref_service, :ref_exists, request, timeout: GitalyClient.fast_timeout)
response.value
rescue GRPC::InvalidArgument => e
raise ArgumentError, e.message
@@ -100,7 +103,7 @@ module Gitlab
name: encode_binary(branch_name)
)
- response = GitalyClient.call(@repository.storage, :ref_service, :find_branch, request, timeout: GitalyClient.medium_timeout)
+ response = gitaly_client_call(@repository.storage, :ref_service, :find_branch, request, timeout: GitalyClient.medium_timeout)
branch = response.branch
return unless branch
@@ -116,7 +119,7 @@ module Gitlab
tag_name: encode_binary(tag_name)
)
- response = GitalyClient.call(@repository.storage, :ref_service, :find_tag, request, timeout: GitalyClient.medium_timeout)
+ response = gitaly_client_call(@repository.storage, :ref_service, :find_tag, request, timeout: GitalyClient.medium_timeout)
tag = response.tag
return unless tag
@@ -140,7 +143,7 @@ module Gitlab
except_with_prefix: except_with_prefixes.map { |r| encode_binary(r) }
)
- response = GitalyClient.call(@repository.storage, :ref_service, :delete_refs, request, timeout: GitalyClient.medium_timeout)
+ response = gitaly_client_call(@repository.storage, :ref_service, :delete_refs, request, timeout: GitalyClient.medium_timeout)
raise Gitlab::Git::Repository::GitError, response.git_error if response.git_error.present?
rescue GRPC::BadStatus => e
@@ -164,7 +167,7 @@ module Gitlab
limit: limit
)
- response = GitalyClient.call(@storage, :ref_service, :list_tag_names_containing_commit, request, timeout: GitalyClient.medium_timeout)
+ response = gitaly_client_call(@storage, :ref_service, :list_tag_names_containing_commit, request, timeout: GitalyClient.medium_timeout)
consume_ref_contains_sha_response(response, :tag_names)
end
@@ -176,7 +179,7 @@ module Gitlab
limit: limit
)
- response = GitalyClient.call(@storage, :ref_service, :list_branch_names_containing_commit, request, timeout: GitalyClient.medium_timeout)
+ response = gitaly_client_call(@storage, :ref_service, :list_branch_names_containing_commit, request, timeout: GitalyClient.medium_timeout)
consume_ref_contains_sha_response(response, :branch_names)
end
@@ -185,7 +188,7 @@ module Gitlab
messages = Hash.new { |h, k| h[k] = +''.b }
current_tag_id = nil
- response = GitalyClient.call(@storage, :ref_service, :get_tag_messages, request, timeout: GitalyClient.fast_timeout)
+ response = gitaly_client_call(@storage, :ref_service, :get_tag_messages, request, timeout: GitalyClient.fast_timeout)
response.each do |rpc_message|
current_tag_id = rpc_message.tag_id if rpc_message.tag_id.present?
@@ -197,7 +200,7 @@ module Gitlab
def get_tag_signatures(tag_ids)
request = Gitaly::GetTagSignaturesRequest.new(repository: @gitaly_repo, tag_revisions: tag_ids)
- response = GitalyClient.call(@repository.storage, :ref_service, :get_tag_signatures, request, timeout: GitalyClient.fast_timeout)
+ response = gitaly_client_call(@repository.storage, :ref_service, :get_tag_signatures, request, timeout: GitalyClient.fast_timeout)
signatures = Hash.new { |h, k| h[k] = [+''.b, +''.b] }
current_tag_id = nil
@@ -222,20 +225,20 @@ module Gitlab
patterns: patterns
)
- response = GitalyClient.call(@storage, :ref_service, :list_refs, request, timeout: GitalyClient.fast_timeout)
+ response = gitaly_client_call(@storage, :ref_service, :list_refs, request, timeout: GitalyClient.fast_timeout)
consume_list_refs_response(response)
end
def pack_refs
request = Gitaly::PackRefsRequest.new(repository: @gitaly_repo)
- GitalyClient.call(@storage, :ref_service, :pack_refs, request, timeout: GitalyClient.long_timeout)
+ gitaly_client_call(@storage, :ref_service, :pack_refs, request, timeout: GitalyClient.long_timeout)
end
def find_refs_by_oid(oid:, limit:, ref_patterns: nil)
request = Gitaly::FindRefsByOIDRequest.new(repository: @gitaly_repo, sort_field: :refname, oid: oid, limit: limit, ref_patterns: ref_patterns)
- response = GitalyClient.call(@storage, :ref_service, :find_refs_by_oid, request, timeout: GitalyClient.medium_timeout)
+ response = gitaly_client_call(@storage, :ref_service, :find_refs_by_oid, request, timeout: GitalyClient.medium_timeout)
response&.refs&.to_a
end
diff --git a/lib/gitlab/gitaly_client/remote_service.rb b/lib/gitlab/gitaly_client/remote_service.rb
index 535b987f91c..9647cfad76e 100644
--- a/lib/gitlab/gitaly_client/remote_service.rb
+++ b/lib/gitlab/gitaly_client/remote_service.rb
@@ -4,6 +4,7 @@ module Gitlab
module GitalyClient
class RemoteService
include Gitlab::EncodingHelper
+ include WithFeatureFlagActors
MAX_MSG_SIZE = 128.kilobytes.freeze
@@ -24,6 +25,8 @@ module Gitlab
@repository = repository
@gitaly_repo = repository.gitaly_repository
@storage = repository.storage
+
+ self.repository_actor = repository
end
def find_remote_root_ref(remote_url, authorization)
@@ -31,7 +34,7 @@ module Gitlab
remote_url: remote_url,
http_authorization_header: authorization)
- response = GitalyClient.call(@storage, :remote_service,
+ response = gitaly_client_call(@storage, :remote_service,
:find_remote_root_ref, request, timeout: GitalyClient.medium_timeout)
encode_utf8(response.ref)
diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb
index 8934067551c..e6565bd33c2 100644
--- a/lib/gitlab/gitaly_client/repository_service.rb
+++ b/lib/gitlab/gitaly_client/repository_service.rb
@@ -4,6 +4,7 @@ module Gitlab
module GitalyClient
class RepositoryService
include Gitlab::EncodingHelper
+ include WithFeatureFlagActors
MAX_MSG_SIZE = 128.kilobytes
@@ -11,57 +12,59 @@ module Gitlab
@repository = repository
@gitaly_repo = repository.gitaly_repository
@storage = repository.storage
+
+ self.repository_actor = repository
end
def exists?
request = Gitaly::RepositoryExistsRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@storage, :repository_service, :repository_exists, request, timeout: GitalyClient.fast_timeout)
+ response = gitaly_client_call(@storage, :repository_service, :repository_exists, request, timeout: GitalyClient.fast_timeout)
response.exists
end
def optimize_repository
request = Gitaly::OptimizeRepositoryRequest.new(repository: @gitaly_repo)
- GitalyClient.call(@storage, :repository_service, :optimize_repository, request, timeout: GitalyClient.long_timeout)
+ gitaly_client_call(@storage, :repository_service, :optimize_repository, request, timeout: GitalyClient.long_timeout)
end
def prune_unreachable_objects
request = Gitaly::PruneUnreachableObjectsRequest.new(repository: @gitaly_repo)
- GitalyClient.call(@storage, :repository_service, :prune_unreachable_objects, request, timeout: GitalyClient.long_timeout)
+ gitaly_client_call(@storage, :repository_service, :prune_unreachable_objects, request, timeout: GitalyClient.long_timeout)
end
def garbage_collect(create_bitmap, prune:)
request = Gitaly::GarbageCollectRequest.new(repository: @gitaly_repo, create_bitmap: create_bitmap, prune: prune)
- GitalyClient.call(@storage, :repository_service, :garbage_collect, request, timeout: GitalyClient.long_timeout)
+ gitaly_client_call(@storage, :repository_service, :garbage_collect, request, timeout: GitalyClient.long_timeout)
end
def repack_full(create_bitmap)
request = Gitaly::RepackFullRequest.new(repository: @gitaly_repo, create_bitmap: create_bitmap)
- GitalyClient.call(@storage, :repository_service, :repack_full, request, timeout: GitalyClient.long_timeout)
+ gitaly_client_call(@storage, :repository_service, :repack_full, request, timeout: GitalyClient.long_timeout)
end
def repack_incremental
request = Gitaly::RepackIncrementalRequest.new(repository: @gitaly_repo)
- GitalyClient.call(@storage, :repository_service, :repack_incremental, request, timeout: GitalyClient.long_timeout)
+ gitaly_client_call(@storage, :repository_service, :repack_incremental, request, timeout: GitalyClient.long_timeout)
end
def repository_size
request = Gitaly::RepositorySizeRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@storage, :repository_service, :repository_size, request, timeout: GitalyClient.long_timeout)
+ response = gitaly_client_call(@storage, :repository_service, :repository_size, request, timeout: GitalyClient.long_timeout)
response.size
end
def get_object_directory_size
request = Gitaly::GetObjectDirectorySizeRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@storage, :repository_service, :get_object_directory_size, request, timeout: GitalyClient.medium_timeout)
+ response = gitaly_client_call(@storage, :repository_service, :get_object_directory_size, request, timeout: GitalyClient.medium_timeout)
response.size
end
def apply_gitattributes(revision)
request = Gitaly::ApplyGitattributesRequest.new(repository: @gitaly_repo, revision: encode_binary(revision))
- GitalyClient.call(@storage, :repository_service, :apply_gitattributes, request, timeout: GitalyClient.fast_timeout)
+ gitaly_client_call(@storage, :repository_service, :apply_gitattributes, request, timeout: GitalyClient.fast_timeout)
rescue GRPC::InvalidArgument => ex
raise Gitlab::Git::Repository::InvalidRef, ex
end
@@ -69,7 +72,7 @@ module Gitlab
def info_attributes
request = Gitaly::GetInfoAttributesRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@storage, :repository_service, :get_info_attributes, request, timeout: GitalyClient.fast_timeout)
+ response = gitaly_client_call(@storage, :repository_service, :get_info_attributes, request, timeout: GitalyClient.fast_timeout)
response.each_with_object([]) do |message, attributes|
attributes << message.attributes
end.join
@@ -103,18 +106,18 @@ module Gitlab
end
end
- GitalyClient.call(@storage, :repository_service, :fetch_remote, request, timeout: GitalyClient.long_timeout)
+ gitaly_client_call(@storage, :repository_service, :fetch_remote, request, timeout: GitalyClient.long_timeout)
end
# rubocop: enable Metrics/ParameterLists
def create_repository(default_branch = nil)
request = Gitaly::CreateRepositoryRequest.new(repository: @gitaly_repo, default_branch: default_branch)
- GitalyClient.call(@storage, :repository_service, :create_repository, request, timeout: GitalyClient.fast_timeout)
+ gitaly_client_call(@storage, :repository_service, :create_repository, request, timeout: GitalyClient.fast_timeout)
end
def has_local_branches?
request = Gitaly::HasLocalBranchesRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@storage, :repository_service, :has_local_branches, request, timeout: GitalyClient.fast_timeout)
+ response = gitaly_client_call(@storage, :repository_service, :has_local_branches, request, timeout: GitalyClient.fast_timeout)
response.value
end
@@ -125,7 +128,7 @@ module Gitlab
revisions: revisions.map { |r| encode_binary(r) }
)
- response = GitalyClient.call(@storage, :repository_service, :find_merge_base, request, timeout: GitalyClient.fast_timeout)
+ response = gitaly_client_call(@storage, :repository_service, :find_merge_base, request, timeout: GitalyClient.fast_timeout)
response.base.presence
end
@@ -135,7 +138,7 @@ module Gitlab
source_repository: source_repository.gitaly_repository
)
- GitalyClient.call(
+ gitaly_client_call(
@storage,
:repository_service,
:create_fork,
@@ -153,7 +156,7 @@ module Gitlab
mirror: mirror
)
- GitalyClient.call(
+ gitaly_client_call(
@storage,
:repository_service,
:create_repository_from_url,
@@ -170,7 +173,7 @@ module Gitlab
target_ref: local_ref.b
)
- response = GitalyClient.call(
+ response = gitaly_client_call(
@storage,
:repository_service,
:fetch_source_branch,
@@ -184,7 +187,7 @@ module Gitlab
def fsck
request = Gitaly::FsckRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@storage, :repository_service, :fsck, request, timeout: GitalyClient.long_timeout)
+ response = gitaly_client_call(@storage, :repository_service, :fsck, request, timeout: GitalyClient.long_timeout)
if response.error.empty?
["", 0]
@@ -236,7 +239,7 @@ module Gitlab
http_auth: http_auth
)
- GitalyClient.call(
+ gitaly_client_call(
@storage,
:repository_service,
:create_repository_from_snapshot,
@@ -253,11 +256,11 @@ module Gitlab
)
request.old_revision = old_ref.b unless old_ref.nil?
- GitalyClient.call(@storage, :repository_service, :write_ref, request, timeout: GitalyClient.fast_timeout)
+ gitaly_client_call(@storage, :repository_service, :write_ref, request, timeout: GitalyClient.fast_timeout)
end
def set_full_path(path)
- GitalyClient.call(
+ gitaly_client_call(
@storage,
:repository_service,
:set_full_path,
@@ -272,7 +275,7 @@ module Gitlab
end
def full_path
- response = GitalyClient.call(
+ response = gitaly_client_call(
@storage,
:repository_service,
:full_path,
@@ -286,12 +289,12 @@ module Gitlab
def find_license
request = Gitaly::FindLicenseRequest.new(repository: @gitaly_repo)
- GitalyClient.call(@storage, :repository_service, :find_license, request, timeout: GitalyClient.medium_timeout)
+ gitaly_client_call(@storage, :repository_service, :find_license, request, timeout: GitalyClient.medium_timeout)
end
def calculate_checksum
request = Gitaly::CalculateChecksumRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@storage, :repository_service, :calculate_checksum, request, timeout: GitalyClient.fast_timeout)
+ response = gitaly_client_call(@storage, :repository_service, :calculate_checksum, request, timeout: GitalyClient.fast_timeout)
response.checksum.presence
rescue GRPC::DataLoss => e
raise Gitlab::Git::Repository::InvalidRepository, e
@@ -300,23 +303,23 @@ module Gitlab
def raw_changes_between(from, to)
request = Gitaly::GetRawChangesRequest.new(repository: @gitaly_repo, from_revision: from, to_revision: to)
- GitalyClient.call(@storage, :repository_service, :get_raw_changes, request, timeout: GitalyClient.fast_timeout)
+ gitaly_client_call(@storage, :repository_service, :get_raw_changes, request, timeout: GitalyClient.fast_timeout)
end
def search_files_by_name(ref, query, limit: 0, offset: 0)
request = Gitaly::SearchFilesByNameRequest.new(repository: @gitaly_repo, ref: ref, query: query, limit: limit, offset: offset)
- GitalyClient.call(@storage, :repository_service, :search_files_by_name, request, timeout: GitalyClient.fast_timeout).flat_map(&:files)
+ gitaly_client_call(@storage, :repository_service, :search_files_by_name, request, timeout: GitalyClient.fast_timeout).flat_map(&:files)
end
def search_files_by_content(ref, query, options = {})
request = Gitaly::SearchFilesByContentRequest.new(repository: @gitaly_repo, ref: ref, query: query)
- response = GitalyClient.call(@storage, :repository_service, :search_files_by_content, request, timeout: GitalyClient.default_timeout)
+ response = gitaly_client_call(@storage, :repository_service, :search_files_by_content, request, timeout: GitalyClient.default_timeout)
search_results_from_response(response, options)
end
def search_files_by_regexp(ref, filter, limit: 0, offset: 0)
request = Gitaly::SearchFilesByNameRequest.new(repository: @gitaly_repo, ref: ref, query: '.', filter: filter, limit: limit, offset: offset)
- GitalyClient.call(@storage, :repository_service, :search_files_by_name, request, timeout: GitalyClient.fast_timeout).flat_map(&:files)
+ gitaly_client_call(@storage, :repository_service, :search_files_by_name, request, timeout: GitalyClient.fast_timeout).flat_map(&:files)
end
def disconnect_alternates
@@ -324,19 +327,19 @@ module Gitlab
repository: @gitaly_repo
)
- GitalyClient.call(@storage, :object_pool_service, :disconnect_git_alternates, request, timeout: GitalyClient.long_timeout)
+ gitaly_client_call(@storage, :object_pool_service, :disconnect_git_alternates, request, timeout: GitalyClient.long_timeout)
end
def rename(relative_path)
request = Gitaly::RenameRepositoryRequest.new(repository: @gitaly_repo, relative_path: relative_path)
- GitalyClient.call(@storage, :repository_service, :rename_repository, request, timeout: GitalyClient.fast_timeout)
+ gitaly_client_call(@storage, :repository_service, :rename_repository, request, timeout: GitalyClient.fast_timeout)
end
def remove
request = Gitaly::RemoveRepositoryRequest.new(repository: @gitaly_repo)
- GitalyClient.call(@storage, :repository_service, :remove_repository, request, timeout: GitalyClient.long_timeout)
+ gitaly_client_call(@storage, :repository_service, :remove_repository, request, timeout: GitalyClient.long_timeout)
end
def replicate(source_repository)
@@ -345,7 +348,7 @@ module Gitlab
source: source_repository.gitaly_repository
)
- GitalyClient.call(
+ gitaly_client_call(
@storage,
:repository_service,
:replicate_repository,
@@ -371,11 +374,11 @@ module Gitlab
current_match << message.match_data
- if message.end_of_match
- matches << current_match
- current_match = +""
- matches_count += 1
- end
+ next unless message.end_of_match
+
+ matches << current_match
+ current_match = +""
+ matches_count += 1
end
matches
@@ -383,7 +386,7 @@ module Gitlab
def gitaly_fetch_stream_to_file(save_path, rpc_name, request_class, timeout)
request = request_class.new(repository: @gitaly_repo)
- response = GitalyClient.call(
+ response = gitaly_client_call(
@storage,
:repository_service,
rpc_name,
@@ -416,7 +419,7 @@ module Gitlab
end
end
- GitalyClient.call(
+ gitaly_client_call(
@storage,
:repository_service,
rpc_name,
diff --git a/lib/gitlab/gitaly_client/with_feature_flag_actors.rb b/lib/gitlab/gitaly_client/with_feature_flag_actors.rb
index f89de276c50..92fc524b724 100644
--- a/lib/gitlab/gitaly_client/with_feature_flag_actors.rb
+++ b/lib/gitlab/gitaly_client/with_feature_flag_actors.rb
@@ -13,6 +13,41 @@ module Gitlab
attr_accessor :repository_actor
+ # gitaly_client_call performs Gitaly calls including collected feature flag actors. The actors are retrieved
+ # from repository actor and memoized. The service must set `self.repository_actor = a_repository` beforehand.
+ def gitaly_client_call(*args, **kargs)
+ return GitalyClient.call(*args, **kargs) unless actors_aware_gitaly_calls?
+
+ unless repository_actor
+ Gitlab::ErrorTracking.track_and_raise_for_dev_exception(
+ Feature::InvalidFeatureFlagError.new("gitaly_client_call called without setting repository_actor")
+ )
+ end
+
+ GitalyClient.with_feature_flag_actors(
+ repository: repository_actor,
+ user: user_actor,
+ project: project_actor,
+ group: group_actor
+ ) do
+ GitalyClient.call(*args, **kargs)
+ end
+ end
+
+ # gitaly_feature_flag_actors returns a hash of actors implied from input repository. If actors_aware_gitaly_calls
+ # flag is not on, this method returns an empty hash.
+ def gitaly_feature_flag_actors(repository)
+ return {} unless actors_aware_gitaly_calls?
+
+ container = find_repository_container(repository)
+ {
+ repository: repository,
+ user: Feature::Gitaly.user_actor,
+ project: Feature::Gitaly.project_actor(container),
+ group: Feature::Gitaly.group_actor(container)
+ }
+ end
+
# Use actor here means the user who originally perform the action. It is collected from ApplicationContext. As
# this information is widely propagated in all entry points, User actor should be available everywhere, even in
# background jobs.
@@ -35,35 +70,32 @@ module Gitlab
end
end
- def gitaly_client_call(*args, **kargs)
- if Feature.enabled?(:actors_aware_gitaly_calls)
- # The order of actors here is significant. Percentage-based actor selection may not work as expected if this
- # order changes.
- GitalyClient.with_feature_flag_actors(
- repository: repository_actor,
- user: user_actor,
- project: project_actor,
- group: group_actor
- ) do
- GitalyClient.call(*args, **kargs)
- end
- else
- GitalyClient.call(*args, **kargs)
- end
- end
+ private
def repository_container
strong_memoize(:repository_container) do
- next if repository_actor&.gl_repository.blank?
+ find_repository_container(repository_actor)
+ end
+ end
+
+ def find_repository_container(repository)
+ return if repository&.gl_repository.blank?
- if repository_actor.container.nil?
- identifier = Gitlab::GlRepository::Identifier.parse(repository_actor.gl_repository)
+ if repository.container.nil?
+ begin
+ identifier = Gitlab::GlRepository::Identifier.parse(repository.gl_repository)
identifier.container
- else
- repository_actor.container
+ rescue Gitlab::GlRepository::Identifier::InvalidIdentifier
+ nil
end
+ else
+ repository.container
end
end
+
+ def actors_aware_gitaly_calls?
+ Feature.enabled?(:actors_aware_gitaly_calls)
+ end
end
end
end
diff --git a/lib/gitlab/import_export/attributes_permitter.rb b/lib/gitlab/import_export/attributes_permitter.rb
index f6f65f85599..8c7a6c13246 100644
--- a/lib/gitlab/import_export/attributes_permitter.rb
+++ b/lib/gitlab/import_export/attributes_permitter.rb
@@ -85,11 +85,11 @@ module Gitlab
while stack.any?
model_name, relations = stack.pop
- if relations.is_a?(Hash)
- add_permitted_attributes(model_name, relations.keys)
+ next unless relations.is_a?(Hash)
- stack.concat(relations.to_a)
- end
+ add_permitted_attributes(model_name, relations.keys)
+
+ stack.concat(relations.to_a)
end
@permitted_attributes
diff --git a/lib/gitlab/import_export/base/relation_object_saver.rb b/lib/gitlab/import_export/base/relation_object_saver.rb
index 3c473449ec0..ed3858d0bf4 100644
--- a/lib/gitlab/import_export/base/relation_object_saver.rb
+++ b/lib/gitlab/import_export/base/relation_object_saver.rb
@@ -81,11 +81,11 @@ module Gitlab
subrelation = relation_object.public_send(definition)
association = relation_object.class.reflect_on_association(definition)
- if association&.collection? && subrelation.size > MIN_RECORDS_SIZE
- collection_subrelations[definition] = subrelation.records
+ next unless association&.collection? && subrelation.size > MIN_RECORDS_SIZE
- subrelation.clear
- end
+ collection_subrelations[definition] = subrelation.records
+
+ subrelation.clear
end
end
end
diff --git a/lib/gitlab/instrumentation/redis_base.rb b/lib/gitlab/instrumentation/redis_base.rb
index 0bd10597f24..268c6cdf459 100644
--- a/lib/gitlab/instrumentation/redis_base.rb
+++ b/lib/gitlab/instrumentation/redis_base.rb
@@ -66,8 +66,8 @@ module Gitlab
query_time.round(::Gitlab::InstrumentationHelper::DURATION_PRECISION)
end
- def redis_cluster_validate!(command)
- ::Gitlab::Instrumentation::RedisClusterValidator.validate!(command) if @redis_cluster_validation
+ def redis_cluster_validate!(commands)
+ ::Gitlab::Instrumentation::RedisClusterValidator.validate!(commands) if @redis_cluster_validation
end
def enable_redis_cluster_validation
diff --git a/lib/gitlab/instrumentation/redis_cluster_validator.rb b/lib/gitlab/instrumentation/redis_cluster_validator.rb
index 005751fb0db..36d3e088956 100644
--- a/lib/gitlab/instrumentation/redis_cluster_validator.rb
+++ b/lib/gitlab/instrumentation/redis_cluster_validator.rb
@@ -10,57 +10,189 @@ module Gitlab
#
# Gitlab::Redis::Cache
# .with { |redis| redis.call('COMMAND') }
- # .select { |command| command[3] != command[4] }
- # .map { |command| [command[0].upcase, { first: command[3], last: command[4], step: command[5] }] }
+ # .select { |cmd| cmd[3] != 0 }
+ # .map { |cmd| [
+ # cmd[0].upcase,
+ # { first: cmd[3], last: cmd[4], step: cmd[5], single_key: cmd[3] == cmd[4] }
+ # ]
+ # }
# .sort_by(&:first)
# .to_h
- #
- MULTI_KEY_COMMANDS = {
- "BITOP" => { first: 2, last: -1, step: 1 },
- "BLPOP" => { first: 1, last: -2, step: 1 },
- "BRPOP" => { first: 1, last: -2, step: 1 },
- "BRPOPLPUSH" => { first: 1, last: 2, step: 1 },
- "BZPOPMAX" => { first: 1, last: -2, step: 1 },
- "BZPOPMIN" => { first: 1, last: -2, step: 1 },
- "DEL" => { first: 1, last: -1, step: 1 },
- "EXISTS" => { first: 1, last: -1, step: 1 },
- "MGET" => { first: 1, last: -1, step: 1 },
- "MSET" => { first: 1, last: -1, step: 2 },
- "MSETNX" => { first: 1, last: -1, step: 2 },
- "PFCOUNT" => { first: 1, last: -1, step: 1 },
- "PFMERGE" => { first: 1, last: -1, step: 1 },
- "RENAME" => { first: 1, last: 2, step: 1 },
- "RENAMENX" => { first: 1, last: 2, step: 1 },
- "RPOPLPUSH" => { first: 1, last: 2, step: 1 },
- "SDIFF" => { first: 1, last: -1, step: 1 },
- "SDIFFSTORE" => { first: 1, last: -1, step: 1 },
- "SINTER" => { first: 1, last: -1, step: 1 },
- "SINTERSTORE" => { first: 1, last: -1, step: 1 },
- "SMOVE" => { first: 1, last: 2, step: 1 },
- "SUNION" => { first: 1, last: -1, step: 1 },
- "SUNIONSTORE" => { first: 1, last: -1, step: 1 },
- "UNLINK" => { first: 1, last: -1, step: 1 },
- "WATCH" => { first: 1, last: -1, step: 1 }
+ REDIS_COMMANDS = {
+ "APPEND" => { first: 1, last: 1, step: 1, single_key: true },
+ "BITCOUNT" => { first: 1, last: 1, step: 1, single_key: true },
+ "BITFIELD" => { first: 1, last: 1, step: 1, single_key: true },
+ "BITFIELD_RO" => { first: 1, last: 1, step: 1, single_key: true },
+ "BITOP" => { first: 2, last: -1, step: 1, single_key: false },
+ "BITPOS" => { first: 1, last: 1, step: 1, single_key: true },
+ "BLMOVE" => { first: 1, last: 2, step: 1, single_key: false },
+ "BLPOP" => { first: 1, last: -2, step: 1, single_key: false },
+ "BRPOP" => { first: 1, last: -2, step: 1, single_key: false },
+ "BRPOPLPUSH" => { first: 1, last: 2, step: 1, single_key: false },
+ "BZPOPMAX" => { first: 1, last: -2, step: 1, single_key: false },
+ "BZPOPMIN" => { first: 1, last: -2, step: 1, single_key: false },
+ "COPY" => { first: 1, last: 2, step: 1, single_key: false },
+ "DECR" => { first: 1, last: 1, step: 1, single_key: true },
+ "DECRBY" => { first: 1, last: 1, step: 1, single_key: true },
+ "DEL" => { first: 1, last: -1, step: 1, single_key: false },
+ "DUMP" => { first: 1, last: 1, step: 1, single_key: true },
+ "EXISTS" => { first: 1, last: -1, step: 1, single_key: false },
+ "EXPIRE" => { first: 1, last: 1, step: 1, single_key: true },
+ "EXPIREAT" => { first: 1, last: 1, step: 1, single_key: true },
+ "GEOADD" => { first: 1, last: 1, step: 1, single_key: true },
+ "GEODIST" => { first: 1, last: 1, step: 1, single_key: true },
+ "GEOHASH" => { first: 1, last: 1, step: 1, single_key: true },
+ "GEOPOS" => { first: 1, last: 1, step: 1, single_key: true },
+ "GEORADIUS" => { first: 1, last: 1, step: 1, single_key: true },
+ "GEORADIUSBYMEMBER" => { first: 1, last: 1, step: 1, single_key: true },
+ "GEORADIUSBYMEMBER_RO" => { first: 1, last: 1, step: 1, single_key: true },
+ "GEORADIUS_RO" => { first: 1, last: 1, step: 1, single_key: true },
+ "GEOSEARCH" => { first: 1, last: 1, step: 1, single_key: true },
+ "GEOSEARCHSTORE" => { first: 1, last: 2, step: 1, single_key: false },
+ "GET" => { first: 1, last: 1, step: 1, single_key: true },
+ "GETBIT" => { first: 1, last: 1, step: 1, single_key: true },
+ "GETDEL" => { first: 1, last: 1, step: 1, single_key: true },
+ "GETEX" => { first: 1, last: 1, step: 1, single_key: true },
+ "GETRANGE" => { first: 1, last: 1, step: 1, single_key: true },
+ "GETSET" => { first: 1, last: 1, step: 1, single_key: true },
+ "HDEL" => { first: 1, last: 1, step: 1, single_key: true },
+ "HEXISTS" => { first: 1, last: 1, step: 1, single_key: true },
+ "HGET" => { first: 1, last: 1, step: 1, single_key: true },
+ "HGETALL" => { first: 1, last: 1, step: 1, single_key: true },
+ "HINCRBY" => { first: 1, last: 1, step: 1, single_key: true },
+ "HINCRBYFLOAT" => { first: 1, last: 1, step: 1, single_key: true },
+ "HKEYS" => { first: 1, last: 1, step: 1, single_key: true },
+ "HLEN" => { first: 1, last: 1, step: 1, single_key: true },
+ "HMGET" => { first: 1, last: 1, step: 1, single_key: true },
+ "HMSET" => { first: 1, last: 1, step: 1, single_key: true },
+ "HRANDFIELD" => { first: 1, last: 1, step: 1, single_key: true },
+ "HSCAN" => { first: 1, last: 1, step: 1, single_key: true },
+ "HSET" => { first: 1, last: 1, step: 1, single_key: true },
+ "HSETNX" => { first: 1, last: 1, step: 1, single_key: true },
+ "HSTRLEN" => { first: 1, last: 1, step: 1, single_key: true },
+ "HVALS" => { first: 1, last: 1, step: 1, single_key: true },
+ "INCR" => { first: 1, last: 1, step: 1, single_key: true },
+ "INCRBY" => { first: 1, last: 1, step: 1, single_key: true },
+ "INCRBYFLOAT" => { first: 1, last: 1, step: 1, single_key: true },
+ "LINDEX" => { first: 1, last: 1, step: 1, single_key: true },
+ "LINSERT" => { first: 1, last: 1, step: 1, single_key: true },
+ "LLEN" => { first: 1, last: 1, step: 1, single_key: true },
+ "LMOVE" => { first: 1, last: 2, step: 1, single_key: false },
+ "LPOP" => { first: 1, last: 1, step: 1, single_key: true },
+ "LPOS" => { first: 1, last: 1, step: 1, single_key: true },
+ "LPUSH" => { first: 1, last: 1, step: 1, single_key: true },
+ "LPUSHX" => { first: 1, last: 1, step: 1, single_key: true },
+ "LRANGE" => { first: 1, last: 1, step: 1, single_key: true },
+ "LREM" => { first: 1, last: 1, step: 1, single_key: true },
+ "LSET" => { first: 1, last: 1, step: 1, single_key: true },
+ "LTRIM" => { first: 1, last: 1, step: 1, single_key: true },
+ "MGET" => { first: 1, last: -1, step: 1, single_key: false },
+ "MIGRATE" => { first: 3, last: 3, step: 1, single_key: true },
+ "MOVE" => { first: 1, last: 1, step: 1, single_key: true },
+ "MSET" => { first: 1, last: -1, step: 2, single_key: false },
+ "MSETNX" => { first: 1, last: -1, step: 2, single_key: false },
+ "OBJECT" => { first: 2, last: 2, step: 1, single_key: true },
+ "PERSIST" => { first: 1, last: 1, step: 1, single_key: true },
+ "PEXPIRE" => { first: 1, last: 1, step: 1, single_key: true },
+ "PEXPIREAT" => { first: 1, last: 1, step: 1, single_key: true },
+ "PFADD" => { first: 1, last: 1, step: 1, single_key: true },
+ "PFCOUNT" => { first: 1, last: -1, step: 1, single_key: false },
+ "PFDEBUG" => { first: 2, last: 2, step: 1, single_key: true },
+ "PFMERGE" => { first: 1, last: -1, step: 1, single_key: false },
+ "PSETEX" => { first: 1, last: 1, step: 1, single_key: true },
+ "PTTL" => { first: 1, last: 1, step: 1, single_key: true },
+ "RENAME" => { first: 1, last: 2, step: 1, single_key: false },
+ "RENAMENX" => { first: 1, last: 2, step: 1, single_key: false },
+ "RESTORE" => { first: 1, last: 1, step: 1, single_key: true },
+ "RESTORE-ASKING" => { first: 1, last: 1, step: 1, single_key: true },
+ "RPOP" => { first: 1, last: 1, step: 1, single_key: true },
+ "RPOPLPUSH" => { first: 1, last: 2, step: 1, single_key: false },
+ "RPUSH" => { first: 1, last: 1, step: 1, single_key: true },
+ "RPUSHX" => { first: 1, last: 1, step: 1, single_key: true },
+ "SADD" => { first: 1, last: 1, step: 1, single_key: true },
+ "SCARD" => { first: 1, last: 1, step: 1, single_key: true },
+ "SDIFF" => { first: 1, last: -1, step: 1, single_key: false },
+ "SDIFFSTORE" => { first: 1, last: -1, step: 1, single_key: false },
+ "SET" => { first: 1, last: 1, step: 1, single_key: true },
+ "SETBIT" => { first: 1, last: 1, step: 1, single_key: true },
+ "SETEX" => { first: 1, last: 1, step: 1, single_key: true },
+ "SETNX" => { first: 1, last: 1, step: 1, single_key: true },
+ "SETRANGE" => { first: 1, last: 1, step: 1, single_key: true },
+ "SINTER" => { first: 1, last: -1, step: 1, single_key: false },
+ "SINTERSTORE" => { first: 1, last: -1, step: 1, single_key: false },
+ "SISMEMBER" => { first: 1, last: 1, step: 1, single_key: true },
+ "SMEMBERS" => { first: 1, last: 1, step: 1, single_key: true },
+ "SMISMEMBER" => { first: 1, last: 1, step: 1, single_key: true },
+ "SMOVE" => { first: 1, last: 2, step: 1, single_key: false },
+ "SORT" => { first: 1, last: 1, step: 1, single_key: true },
+ "SPOP" => { first: 1, last: 1, step: 1, single_key: true },
+ "SRANDMEMBER" => { first: 1, last: 1, step: 1, single_key: true },
+ "SREM" => { first: 1, last: 1, step: 1, single_key: true },
+ "SSCAN" => { first: 1, last: 1, step: 1, single_key: true },
+ "STRLEN" => { first: 1, last: 1, step: 1, single_key: true },
+ "SUBSTR" => { first: 1, last: 1, step: 1, single_key: true },
+ "SUNION" => { first: 1, last: -1, step: 1, single_key: false },
+ "SUNIONSTORE" => { first: 1, last: -1, step: 1, single_key: false },
+ "TOUCH" => { first: 1, last: -1, step: 1, single_key: false },
+ "TTL" => { first: 1, last: 1, step: 1, single_key: true },
+ "TYPE" => { first: 1, last: 1, step: 1, single_key: true },
+ "UNLINK" => { first: 1, last: -1, step: 1, single_key: false },
+ "WATCH" => { first: 1, last: -1, step: 1, single_key: false },
+ "XACK" => { first: 1, last: 1, step: 1, single_key: true },
+ "XADD" => { first: 1, last: 1, step: 1, single_key: true },
+ "XAUTOCLAIM" => { first: 1, last: 1, step: 1, single_key: true },
+ "XCLAIM" => { first: 1, last: 1, step: 1, single_key: true },
+ "XDEL" => { first: 1, last: 1, step: 1, single_key: true },
+ "XGROUP" => { first: 2, last: 2, step: 1, single_key: true },
+ "XINFO" => { first: 2, last: 2, step: 1, single_key: true },
+ "XLEN" => { first: 1, last: 1, step: 1, single_key: true },
+ "XPENDING" => { first: 1, last: 1, step: 1, single_key: true },
+ "XRANGE" => { first: 1, last: 1, step: 1, single_key: true },
+ "XREVRANGE" => { first: 1, last: 1, step: 1, single_key: true },
+ "XSETID" => { first: 1, last: 1, step: 1, single_key: true },
+ "XTRIM" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZADD" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZCARD" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZCOUNT" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZDIFFSTORE" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZINCRBY" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZINTERSTORE" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZLEXCOUNT" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZMSCORE" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZPOPMAX" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZPOPMIN" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZRANDMEMBER" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZRANGE" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZRANGEBYLEX" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZRANGEBYSCORE" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZRANGESTORE" => { first: 1, last: 2, step: 1, single_key: false },
+ "ZRANK" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZREM" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZREMRANGEBYLEX" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZREMRANGEBYRANK" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZREMRANGEBYSCORE" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZREVRANGE" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZREVRANGEBYLEX" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZREVRANGEBYSCORE" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZREVRANK" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZSCAN" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZSCORE" => { first: 1, last: 1, step: 1, single_key: true },
+ "ZUNIONSTORE" => { first: 1, last: 1, step: 1, single_key: true }
}.freeze
CrossSlotError = Class.new(StandardError)
class << self
- def validate!(command)
+ def validate!(commands)
return unless Rails.env.development? || Rails.env.test?
return if allow_cross_slot_commands?
+ return if commands.empty?
- command_name = command.first.to_s.upcase
- argument_positions = MULTI_KEY_COMMANDS[command_name]
-
- return unless argument_positions
-
- arguments = command.flatten[argument_positions[:first]..argument_positions[:last]]
-
- key_slots = arguments.each_slice(argument_positions[:step]).map do |args|
- key_slot(args.first)
- end
+ # early exit for single-command (non-pipelined) if it is a single-key-command
+ command_name = commands.size > 1 ? "PIPELINE/MULTI" : commands.first.first.to_s.upcase
+ return if commands.size == 1 && REDIS_COMMANDS.dig(command_name, :single_key)
+ key_slots = commands.map { |command| key_slots(command) }.flatten
if key_slots.uniq.many? # rubocop: disable CodeReuse/ActiveRecord
raise CrossSlotError, "Redis command #{command_name} arguments hash to different slots. See https://docs.gitlab.com/ee/development/redis.html#multi-key-commands"
end
@@ -78,6 +210,17 @@ module Gitlab
private
+ def key_slots(command)
+ argument_positions = REDIS_COMMANDS[command.first.to_s.upcase]
+
+ return [] unless argument_positions
+
+ arguments = command.flatten[argument_positions[:first]..argument_positions[:last]]
+ arguments.each_slice(argument_positions[:step]).map do |args|
+ key_slot(args.first)
+ end
+ end
+
def allow_cross_slot_commands?
Thread.current[:allow_cross_slot_commands].to_i > 0
end
diff --git a/lib/gitlab/instrumentation/redis_interceptor.rb b/lib/gitlab/instrumentation/redis_interceptor.rb
index 02c96d632b0..f19279df2fe 100644
--- a/lib/gitlab/instrumentation/redis_interceptor.rb
+++ b/lib/gitlab/instrumentation/redis_interceptor.rb
@@ -33,8 +33,7 @@ module Gitlab
def instrument_call(commands)
start = Gitlab::Metrics::System.monotonic_time # must come first so that 'start' is always defined
instrumentation_class.instance_count_request(commands.size)
-
- commands.each { |c| instrumentation_class.redis_cluster_validate!(c) }
+ instrumentation_class.redis_cluster_validate!(commands)
yield
rescue ::Redis::BaseError => ex
diff --git a/lib/gitlab/issues/rebalancing/state.rb b/lib/gitlab/issues/rebalancing/state.rb
index 99f777edee5..36346564b39 100644
--- a/lib/gitlab/issues/rebalancing/state.rb
+++ b/lib/gitlab/issues/rebalancing/state.rb
@@ -99,11 +99,13 @@ module Gitlab
def refresh_keys_expiration
with_redis do |redis|
- redis.multi do |multi|
- multi.expire(issue_ids_key, REDIS_EXPIRY_TIME)
- multi.expire(current_index_key, REDIS_EXPIRY_TIME)
- multi.expire(current_project_key, REDIS_EXPIRY_TIME)
- multi.expire(CONCURRENT_RUNNING_REBALANCES_KEY, REDIS_EXPIRY_TIME)
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ redis.multi do |multi|
+ multi.expire(issue_ids_key, REDIS_EXPIRY_TIME)
+ multi.expire(current_index_key, REDIS_EXPIRY_TIME)
+ multi.expire(current_project_key, REDIS_EXPIRY_TIME)
+ multi.expire(CONCURRENT_RUNNING_REBALANCES_KEY, REDIS_EXPIRY_TIME)
+ end
end
end
end
@@ -112,12 +114,14 @@ module Gitlab
value = "#{rebalanced_container_type}/#{rebalanced_container_id}"
with_redis do |redis|
- redis.multi do |multi|
- multi.del(issue_ids_key)
- multi.del(current_index_key)
- multi.del(current_project_key)
- multi.srem?(CONCURRENT_RUNNING_REBALANCES_KEY, value)
- multi.set(self.class.recently_finished_key(rebalanced_container_type, rebalanced_container_id), true, ex: 1.hour)
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ redis.multi do |multi|
+ multi.del(issue_ids_key)
+ multi.del(current_index_key)
+ multi.del(current_project_key)
+ multi.srem?(CONCURRENT_RUNNING_REBALANCES_KEY, value)
+ multi.set(self.class.recently_finished_key(rebalanced_container_type, rebalanced_container_id), true, ex: 1.hour)
+ end
end
end
end
diff --git a/lib/gitlab/manifest_import/metadata.rb b/lib/gitlab/manifest_import/metadata.rb
index 6fe9bb10cdf..3747431c6a7 100644
--- a/lib/gitlab/manifest_import/metadata.rb
+++ b/lib/gitlab/manifest_import/metadata.rb
@@ -14,9 +14,11 @@ module Gitlab
def save(repositories, group_id)
Gitlab::Redis::SharedState.with do |redis|
- redis.multi do |multi|
- multi.set(key_for('repositories'), Gitlab::Json.dump(repositories), ex: EXPIRY_TIME)
- multi.set(key_for('group_id'), group_id, ex: EXPIRY_TIME)
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ redis.multi do |multi|
+ multi.set(key_for('repositories'), Gitlab::Json.dump(repositories), ex: EXPIRY_TIME)
+ multi.set(key_for('group_id'), group_id, ex: EXPIRY_TIME)
+ end
end
end
end
diff --git a/lib/gitlab/markdown_cache/redis/store.rb b/lib/gitlab/markdown_cache/redis/store.rb
index fd5870fa842..8cab069e1bf 100644
--- a/lib/gitlab/markdown_cache/redis/store.rb
+++ b/lib/gitlab/markdown_cache/redis/store.rb
@@ -10,9 +10,11 @@ module Gitlab
results = {}
Gitlab::Redis::Cache.with do |r|
- r.pipelined do |pipeline|
- subjects.each do |subject|
- results[subject.cache_key] = new(subject).read(pipeline)
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ r.pipelined do |pipeline|
+ subjects.each do |subject|
+ results[subject.cache_key] = new(subject).read(pipeline)
+ end
end
end
end
diff --git a/lib/gitlab/metrics/samplers/base_sampler.rb b/lib/gitlab/metrics/samplers/base_sampler.rb
index b2a9de21145..e62a62a935e 100644
--- a/lib/gitlab/metrics/samplers/base_sampler.rb
+++ b/lib/gitlab/metrics/samplers/base_sampler.rb
@@ -46,11 +46,11 @@ module Gitlab
# 2. Don't sample data at the same interval two times in a row.
def sleep_interval
while step = @interval_steps.sample
- if step != @last_step
- @last_step = step
+ next if step == @last_step
- return @interval + @last_step
- end
+ @last_step = step
+
+ return @interval + @last_step
end
end
diff --git a/lib/gitlab/pagination/keyset/in_operator_optimization/strategies/record_loader_strategy.rb b/lib/gitlab/pagination/keyset/in_operator_optimization/strategies/record_loader_strategy.rb
index 51f38c1da58..4f79a3593f4 100644
--- a/lib/gitlab/pagination/keyset/in_operator_optimization/strategies/record_loader_strategy.rb
+++ b/lib/gitlab/pagination/keyset/in_operator_optimization/strategies/record_loader_strategy.rb
@@ -39,15 +39,15 @@ module Gitlab
def verify_order_by_attributes_on_model!(model, order_by_columns)
order_by_columns.map(&:column).each do |column|
- unless model.columns_hash[column.attribute_name.to_s]
- text = <<~TEXT
+ next if model.columns_hash[column.attribute_name.to_s]
+
+ text = <<~TEXT
The "RecordLoaderStrategy" does not support the following ORDER BY column because
it's not available on the \"#{model.table_name}\" table: #{column.attribute_name}
Omit the "finder_query" parameter to use the "OrderValuesLoaderStrategy".
- TEXT
- raise text
- end
+ TEXT
+ raise text
end
end
end
diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb
index f914123a94d..c5798bec0d7 100644
--- a/lib/gitlab/reference_extractor.rb
+++ b/lib/gitlab/reference_extractor.rb
@@ -42,10 +42,10 @@ module Gitlab
@references[type] ||= references(type)
end
- if %w(mentioned_user mentioned_group mentioned_project).include?(type.to_s)
- define_method("#{type}_ids") do
- @references[type] ||= references(type, ids_only: true)
- end
+ next unless %w(mentioned_user mentioned_group mentioned_project).include?(type.to_s)
+
+ define_method("#{type}_ids") do
+ @references[type] ||= references(type, ids_only: true)
end
end
diff --git a/lib/tasks/gitlab/assets.rake b/lib/tasks/gitlab/assets.rake
index 3d3bdc560ac..12a8cb01e9e 100644
--- a/lib/tasks/gitlab/assets.rake
+++ b/lib/tasks/gitlab/assets.rake
@@ -127,20 +127,20 @@ namespace :gitlab do
# rewrite the corresponding gzip file (if it exists)
gzip = "#{file}.gz"
- if File.exist?(gzip)
- puts "Fixing #{gzip}"
+ next unless File.exist?(gzip)
- FileUtils.rm(gzip)
- mtime = File.stat(file).mtime
+ puts "Fixing #{gzip}"
- File.open(gzip, 'wb+') do |f|
- gz = Zlib::GzipWriter.new(f, Zlib::BEST_COMPRESSION)
- gz.mtime = mtime
- gz.write IO.binread(file)
- gz.close
+ FileUtils.rm(gzip)
+ mtime = File.stat(file).mtime
- File.utime(mtime, mtime, f.path)
- end
+ File.open(gzip, 'wb+') do |f|
+ gz = Zlib::GzipWriter.new(f, Zlib::BEST_COMPRESSION)
+ gz.mtime = mtime
+ gz.write IO.binread(file)
+ gz.close
+
+ File.utime(mtime, mtime, f.path)
end
end
end
diff --git a/lib/tasks/gitlab/db/validate_config.rake b/lib/tasks/gitlab/db/validate_config.rake
index bf9ebc56486..b3c98e91d17 100644
--- a/lib/tasks/gitlab/db/validate_config.rake
+++ b/lib/tasks/gitlab/db/validate_config.rake
@@ -64,15 +64,15 @@ namespace :gitlab do
next unless identifier
connections_with_tasks = connections.select { |connection| connection[:database_tasks?] }
- if connections_with_tasks.many?
- names = connections_with_tasks.pluck(:name)
-
- warnings << "- Many configurations (#{names.join(', ')}) " \
- "share the same database (#{identifier}). " \
- "This will result in failures provisioning or migrating this database. " \
- "Ensure that additional databases are configured " \
- "with 'database_tasks: false' or are pointing to a dedicated database host."
- end
+ next unless connections_with_tasks.many?
+
+ names = connections_with_tasks.pluck(:name)
+
+ warnings << "- Many configurations (#{names.join(', ')}) " \
+ "share the same database (#{identifier}). " \
+ "This will result in failures provisioning or migrating this database. " \
+ "Ensure that additional databases are configured " \
+ "with 'database_tasks: false' or are pointing to a dedicated database host."
end
# Each configuration with `database_tasks: false` should share the database with `main:`
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index a2b05faa74f..3fa779062d7 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -17232,6 +17232,9 @@ msgstr ""
msgid "Followed users"
msgstr ""
+msgid "Following tags don't exist"
+msgstr ""
+
msgid "Font Color"
msgstr ""
@@ -36653,6 +36656,9 @@ msgstr ""
msgid "SecurityOrchestration|Use a scan execution policy to create rules which enforce security scans for particular branches at a certain time. Supported types are SAST, DAST, Secret detection, Container scanning, and Dependency scanning."
msgstr ""
+msgid "SecurityOrchestration|Use a scan result policy to create rules that check for security vulnerabilities and license compliance before merging a merge request."
+msgstr ""
+
msgid "SecurityOrchestration|Use a scan result policy to create rules that ensure security issues are checked before merging a merge request."
msgstr ""
@@ -36716,6 +36722,9 @@ msgstr ""
msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
+msgid "SecurityReports|Activity"
+msgstr ""
+
msgid "SecurityReports|Add or remove projects to monitor in the security area. Projects included in this list will have their results displayed in the security dashboard and vulnerability report."
msgstr ""
@@ -47867,10 +47876,10 @@ msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
+msgid "ciReport|%{scanner} detected no new %{vulnStr}"
msgstr ""
-msgid "ciReport|%{scanner} detected no new %{vulnStr}"
+msgid "ciReport|%{scanner} detected no new potential vulnerabilities"
msgstr ""
msgid "ciReport|: Loading resulted in an error"
diff --git a/package.json b/package.json
index 0fe20b96301..7b28a3d557b 100644
--- a/package.json
+++ b/package.json
@@ -53,8 +53,8 @@
"@codesandbox/sandpack-client": "^1.2.2",
"@gitlab/at.js": "1.5.7",
"@gitlab/favicon-overlay": "2.0.0",
- "@gitlab/svgs": "3.7.0",
- "@gitlab/ui": "49.3.0",
+ "@gitlab/svgs": "3.8.0",
+ "@gitlab/ui": "49.10.0",
"@gitlab/visual-review-tools": "1.7.3",
"@gitlab/web-ide": "0.0.1-dev-20221114183058",
"@rails/actioncable": "6.1.4-7",
diff --git a/scripts/perf/query_limiting_report.rb b/scripts/perf/query_limiting_report.rb
index 364cd6fc5d4..6326b2590ae 100755
--- a/scripts/perf/query_limiting_report.rb
+++ b/scripts/perf/query_limiting_report.rb
@@ -124,19 +124,19 @@ class QueryLimitingReport
file_lines.each_index do |index|
line = file_lines[index]
- if line =~ /#{CODE_LINES_SEARCH_STRING}/o
- issue_iid = line.slice(%r{issues/(\d+)\D}, 1)
- line_number = index + 1
- code_line = {
- file_location: "#{filename}:#{line_number}",
- filename: filename,
- line_number: line_number,
- line: line,
- issue_iid: issue_iid.to_i,
- has_issue_iid: !issue_iid.nil?
- }
- code_lines << code_line
- end
+ next unless line =~ /#{CODE_LINES_SEARCH_STRING}/o
+
+ issue_iid = line.slice(%r{issues/(\d+)\D}, 1)
+ line_number = index + 1
+ code_line = {
+ file_location: "#{filename}:#{line_number}",
+ filename: filename,
+ line_number: line_number,
+ line: line,
+ issue_iid: issue_iid.to_i,
+ has_issue_iid: !issue_iid.nil?
+ }
+ code_lines << code_line
end
end
diff --git a/scripts/qa/quarantine-types-check b/scripts/qa/quarantine-types-check
index 44d329a3590..188348b949c 100755
--- a/scripts/qa/quarantine-types-check
+++ b/scripts/qa/quarantine-types-check
@@ -30,19 +30,19 @@ puts "\nAnalyzing quarantined test data...\n"
tests = data_hash['examples']
tests.each do |test|
- if test['quarantine']
- unless QUARANTINE_TYPES.include?(test['quarantine']['type'])
- quarantine_type_errors.push(
- <<~TYPE_ERRORS
+ next unless test['quarantine']
+
+ unless QUARANTINE_TYPES.include?(test['quarantine']['type'])
+ quarantine_type_errors.push(
+ <<~TYPE_ERRORS
==> #{test['full_description']}
in file: #{test['id']}
with type: "#{test['quarantine']['type']}"
- TYPE_ERRORS
- )
- end
-
- missing_issues.push(" ==> #{test['id']} - #{test['full_description']}\n") unless test['quarantine']['issue']
+ TYPE_ERRORS
+ )
end
+
+ missing_issues.push(" ==> #{test['id']} - #{test['full_description']}\n") unless test['quarantine']['issue']
end
if quarantine_type_errors.empty? && missing_issues.empty?
diff --git a/scripts/review_apps/automated_cleanup.rb b/scripts/review_apps/automated_cleanup.rb
index 2440df6958d..f020283de52 100755
--- a/scripts/review_apps/automated_cleanup.rb
+++ b/scripts/review_apps/automated_cleanup.rb
@@ -88,7 +88,7 @@ module ReviewApps
if deployed_at < delete_threshold
deleted_environment = delete_environment(environment, deployment)
if deleted_environment
- release = Tooling::Helm3Client::Release.new(environment.slug, 1, deployed_at.to_s, nil, nil, review_apps_namespace)
+ release = Tooling::Helm3Client::Release.new(environment.slug, 1, deployed_at.to_s, nil, nil, environment.slug)
releases_to_delete << release
end
else
@@ -104,7 +104,7 @@ module ReviewApps
end
delete_stopped_environments(environment_type: :review_app, checked_environments: checked_environments, last_updated_threshold: delete_threshold) do |environment|
- releases_to_delete << Tooling::Helm3Client::Release.new(environment.slug, 1, environment.updated_at, nil, nil, review_apps_namespace)
+ releases_to_delete << Tooling::Helm3Client::Release.new(environment.slug, 1, environment.updated_at, nil, nil, environment.slug)
end
delete_helm_releases(releases_to_delete)
@@ -190,6 +190,8 @@ module ReviewApps
rescue Gitlab::Error::Forbidden
puts "Review app '#{environment.name}' / '#{environment.slug}' (##{environment.id}) is forbidden: skipping it"
+ rescue Gitlab::Error::InternalServerError
+ puts "Review app '#{environment.name}' / '#{environment.slug}' (##{environment.id}) 500 error - ignoring it"
end
def stop_environment(environment, deployment)
diff --git a/scripts/review_apps/base-config.yaml b/scripts/review_apps/base-config.yaml
index 132158d6e8c..43dc562c58a 100644
--- a/scripts/review_apps/base-config.yaml
+++ b/scripts/review_apps/base-config.yaml
@@ -18,6 +18,7 @@ global:
preemptible: "true"
certmanager:
install: false
+
gitlab:
gitaly:
resources:
@@ -34,18 +35,10 @@ gitlab:
preemptible: "false"
podAnnotations:
<<: *safe-to-evict
+
gitlab-exporter:
enabled: false
- mailroom:
- enabled: false
- migrations:
- resources:
- requests:
- cpu: 400m
- memory: 920Mi
- limits:
- cpu: 600m
- memory: 1100Mi
+
gitlab-shell:
resources:
requests:
@@ -54,12 +47,30 @@ gitlab:
limits:
cpu: 750m
memory: 150Mi
- maxReplicas: 3
+ minReplicas: 1
+ maxReplicas: 1
hpa:
targetAverageValue: 500m
deployment:
livenessProbe:
timeoutSeconds: 5
+
+ kas:
+ minReplicas: 1
+ maxReplicas: 1
+
+ mailroom:
+ enabled: false
+
+ migrations:
+ resources:
+ requests:
+ cpu: 400m
+ memory: 920Mi
+ limits:
+ cpu: 600m
+ memory: 1100Mi
+
sidekiq:
resources:
requests:
@@ -70,6 +81,7 @@ gitlab:
memory: 2890Mi
hpa:
targetAverageValue: 650m
+
toolbox:
resources:
requests:
@@ -78,6 +90,7 @@ gitlab:
limits:
cpu: 450m
memory: 2890Mi
+
webservice:
resources:
requests:
@@ -86,6 +99,8 @@ gitlab:
limits:
cpu: 1119m
memory: 4214Mi
+ minReplicas: 1
+ maxReplicas: 1
deployment:
readinessProbe:
initialDelaySeconds: 5 # Default is 0
@@ -103,6 +118,7 @@ gitlab:
initialDelaySeconds: 5 # Default is 0
periodSeconds: 15 # Default is 10
timeoutSeconds: 5 # Default is 2
+
gitlab-runner:
resources:
requests:
@@ -115,6 +131,7 @@ gitlab-runner:
preemptible: "true"
podAnnotations:
<<: *safe-to-evict
+
minio:
resources:
requests:
@@ -127,6 +144,7 @@ minio:
preemptible: "true"
podAnnotations:
<<: *safe-to-evict
+
nginx-ingress:
controller:
config:
@@ -156,6 +174,7 @@ nginx-ingress:
memory: 24Mi
nodeSelector:
preemptible: "true"
+
postgresql:
metrics:
enabled: false
@@ -171,8 +190,10 @@ postgresql:
preemptible: "false"
podAnnotations:
<<: *safe-to-evict
+
prometheus:
install: false
+
redis:
metrics:
enabled: false
@@ -188,9 +209,11 @@ redis:
preemptible: "true"
podAnnotations:
<<: *safe-to-evict
+
registry:
hpa:
minReplicas: 1
+ maxReplicas: 1
resources:
requests:
cpu: 100m
diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh
index 7094bfcdf1c..5883141a943 100755
--- a/scripts/review_apps/review-apps.sh
+++ b/scripts/review_apps/review-apps.sh
@@ -62,7 +62,7 @@ function previous_deploy_failed() {
return $status
}
-function delete_release() {
+function delete_helm_release() {
local namespace="${CI_ENVIRONMENT_SLUG}"
local release="${CI_ENVIRONMENT_SLUG}"
@@ -74,32 +74,6 @@ function delete_release() {
if deploy_exists "${namespace}" "${release}"; then
helm uninstall --namespace="${namespace}" "${release}"
fi
-}
-
-function delete_failed_release() {
- local namespace="${CI_ENVIRONMENT_SLUG}"
- local release="${CI_ENVIRONMENT_SLUG}"
-
- if [ -z "${release}" ]; then
- echoerr "No release given, aborting the delete!"
- return
- fi
-
- if ! deploy_exists "${namespace}" "${release}"; then
- echoinfo "No Review App with ${release} is currently deployed."
- else
- # Cleanup and previous installs, as FAILED and PENDING_UPGRADE will cause errors with `upgrade`
- if previous_deploy_failed "${namespace}" "${release}" ; then
- echoinfo "Review App deployment in bad state, cleaning up namespace ${release}"
- delete_namespace
- else
- echoinfo "Review App deployment in good state"
- fi
- fi
-}
-
-function delete_namespace() {
- local namespace="${CI_ENVIRONMENT_SLUG}"
if namespace_exists "${namespace}"; then
echoinfo "Deleting namespace ${namespace}..." true
diff --git a/spec/fixtures/api/schemas/public_api/v4/metadata.json b/spec/fixtures/api/schemas/public_api/v4/metadata.json
index fd219b95df8..f5a6aa86890 100644
--- a/spec/fixtures/api/schemas/public_api/v4/metadata.json
+++ b/spec/fixtures/api/schemas/public_api/v4/metadata.json
@@ -3,11 +3,16 @@
"required": [
"version",
"revision",
- "kas"
+ "kas",
+ "enterprise"
],
"properties": {
- "version": { "type": "string" },
- "revision": { "type": "string" },
+ "version": {
+ "type": "string"
+ },
+ "revision": {
+ "type": "string"
+ },
"kas": {
"type": "object",
"required": [
@@ -16,11 +21,26 @@
"version"
],
"properties": {
- "enabled": { "type": "boolean" },
- "externalUrl": { "type": ["string", "null"] },
- "version": { "type": ["string", "null"] }
+ "enabled": {
+ "type": "boolean"
+ },
+ "externalUrl": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "version": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
}
+ },
+ "enterprise": {
+ "type": "boolean"
}
},
"additionalProperties": false
-}
+} \ No newline at end of file
diff --git a/spec/frontend/access_tokens/components/__snapshots__/expires_at_field_spec.js.snap b/spec/frontend/access_tokens/components/__snapshots__/expires_at_field_spec.js.snap
index 2bd2b17a12d..42818c14029 100644
--- a/spec/frontend/access_tokens/components/__snapshots__/expires_at_field_spec.js.snap
+++ b/spec/frontend/access_tokens/components/__snapshots__/expires_at_field_spec.js.snap
@@ -21,6 +21,7 @@ exports[`~/access_tokens/components/expires_at_field should render datepicker wi
mindate="Mon Jul 06 2020 00:00:00 GMT+0000 (Greenwich Mean Time)"
placeholder="YYYY-MM-DD"
showclearbutton="true"
+ size="medium"
theme=""
/>
</gl-form-group-stub>
diff --git a/spec/frontend/ci_variable_list/components/ci_admin_variables_spec.js b/spec/frontend/ci_variable_list/components/ci_admin_variables_spec.js
index c80e1184f02..c7375acd8e5 100644
--- a/spec/frontend/ci_variable_list/components/ci_admin_variables_spec.js
+++ b/spec/frontend/ci_variable_list/components/ci_admin_variables_spec.js
@@ -24,6 +24,7 @@ describe('Ci Project Variable wrapper', () => {
expect(findCiShared().props()).toEqual({
areScopedVariablesAvailable: false,
componentName: 'InstanceVariables',
+ hideEnvironmentScope: true,
mutationData: wrapper.vm.$options.mutationData,
queryData: wrapper.vm.$options.queryData,
refetchAfterMutation: true,
diff --git a/spec/frontend/ci_variable_list/components/ci_group_variables_spec.js b/spec/frontend/ci_variable_list/components/ci_group_variables_spec.js
index 525cba3424b..ef5a86ccb61 100644
--- a/spec/frontend/ci_variable_list/components/ci_group_variables_spec.js
+++ b/spec/frontend/ci_variable_list/components/ci_group_variables_spec.js
@@ -40,6 +40,7 @@ describe('Ci Group Variable wrapper', () => {
areScopedVariablesAvailable: false,
componentName: 'GroupVariables',
fullPath: mockProvide.groupPath,
+ hideEnvironmentScope: false,
mutationData: wrapper.vm.$options.mutationData,
queryData: wrapper.vm.$options.queryData,
refetchAfterMutation: false,
diff --git a/spec/frontend/ci_variable_list/components/ci_project_variables_spec.js b/spec/frontend/ci_variable_list/components/ci_project_variables_spec.js
index 984baa45d91..97051325f59 100644
--- a/spec/frontend/ci_variable_list/components/ci_project_variables_spec.js
+++ b/spec/frontend/ci_variable_list/components/ci_project_variables_spec.js
@@ -36,6 +36,7 @@ describe('Ci Project Variable wrapper', () => {
areScopedVariablesAvailable: true,
componentName: 'ProjectVariables',
fullPath: mockProvide.projectFullPath,
+ hideEnvironmentScope: false,
mutationData: wrapper.vm.$options.mutationData,
queryData: wrapper.vm.$options.queryData,
refetchAfterMutation: false,
diff --git a/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js b/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js
index 1ea4e4f833b..e4771f040d1 100644
--- a/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js
+++ b/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js
@@ -39,6 +39,7 @@ describe('Ci variable modal', () => {
const defaultProps = {
areScopedVariablesAvailable: true,
environments: [],
+ hideEnvironmentScope: false,
mode: ADD_VARIABLE_ACTION,
selectedVariable: {},
variable: [],
@@ -75,6 +76,7 @@ describe('Ci variable modal', () => {
const findEnvScopeInput = () =>
wrapper.findByTestId('environment-scope').findComponent(GlFormInput);
const findVariableTypeDropdown = () => wrapper.find('#ci-variable-type');
+ const findEnvironmentScopeText = () => wrapper.findByText('Environment scope');
afterEach(() => {
wrapper.destroy();
@@ -250,39 +252,83 @@ describe('Ci variable modal', () => {
describe('Environment scope', () => {
describe('when feature is available', () => {
- it('renders the environment dropdown', () => {
- createComponent({
- mountFn: mountExtended,
- props: {
- areScopedVariablesAvailable: true,
- },
+ describe('and section is not hidden', () => {
+ beforeEach(() => {
+ createComponent({
+ mountFn: mountExtended,
+ props: {
+ areScopedVariablesAvailable: true,
+ hideEnvironmentScope: false,
+ },
+ });
});
- expect(findCiEnvironmentsDropdown().exists()).toBe(true);
- expect(findCiEnvironmentsDropdown().isVisible()).toBe(true);
- });
+ it('renders the environment dropdown and section title', () => {
+ expect(findCiEnvironmentsDropdown().exists()).toBe(true);
+ expect(findCiEnvironmentsDropdown().isVisible()).toBe(true);
+ expect(findEnvironmentScopeText().exists()).toBe(true);
+ });
- it('renders a link to documentation on scopes', () => {
- createComponent({ mountFn: mountExtended });
+ it('renders a link to documentation on scopes', () => {
+ const link = findEnvScopeLink();
+
+ expect(link.attributes('title')).toBe(ENVIRONMENT_SCOPE_LINK_TITLE);
+ expect(link.attributes('href')).toBe(defaultProvide.environmentScopeLink);
+ });
+ });
- const link = findEnvScopeLink();
+ describe('and section is hidden', () => {
+ beforeEach(() => {
+ createComponent({
+ mountFn: mountExtended,
+ props: {
+ areScopedVariablesAvailable: true,
+ hideEnvironmentScope: true,
+ },
+ });
+ });
- expect(link.attributes('title')).toBe(ENVIRONMENT_SCOPE_LINK_TITLE);
- expect(link.attributes('href')).toBe(defaultProvide.environmentScopeLink);
+ it('does not renders the environment dropdown and section title', () => {
+ expect(findCiEnvironmentsDropdown().exists()).toBe(false);
+ expect(findEnvironmentScopeText().exists()).toBe(false);
+ });
});
});
describe('when feature is not available', () => {
- it('disables the dropdown', () => {
- createComponent({
- mountFn: mountExtended,
- props: {
- areScopedVariablesAvailable: false,
- },
+ describe('and section is not hidden', () => {
+ beforeEach(() => {
+ createComponent({
+ mountFn: mountExtended,
+ props: {
+ areScopedVariablesAvailable: false,
+ hideEnvironmentScope: false,
+ },
+ });
});
- expect(findCiEnvironmentsDropdown().exists()).toBe(false);
- expect(findEnvScopeInput().attributes('readonly')).toBe('readonly');
+ it('disables the dropdown', () => {
+ expect(findCiEnvironmentsDropdown().exists()).toBe(false);
+ expect(findEnvironmentScopeText().exists()).toBe(true);
+ expect(findEnvScopeInput().attributes('readonly')).toBe('readonly');
+ });
+ });
+
+ describe('and section is hidden', () => {
+ beforeEach(() => {
+ createComponent({
+ mountFn: mountExtended,
+ props: {
+ areScopedVariablesAvailable: false,
+ hideEnvironmentScope: true,
+ },
+ });
+ });
+
+ it('hides the dropdown', () => {
+ expect(findEnvironmentScopeText().exists()).toBe(false);
+ expect(findCiEnvironmentsDropdown().exists()).toBe(false);
+ });
});
});
});
diff --git a/spec/frontend/ci_variable_list/components/ci_variable_settings_spec.js b/spec/frontend/ci_variable_list/components/ci_variable_settings_spec.js
index 5c77ce71b41..8b5a0f7ae9d 100644
--- a/spec/frontend/ci_variable_list/components/ci_variable_settings_spec.js
+++ b/spec/frontend/ci_variable_list/components/ci_variable_settings_spec.js
@@ -18,6 +18,7 @@ describe('Ci variable table', () => {
const defaultProps = {
areScopedVariablesAvailable: true,
environments: mapEnvironmentNames(mockEnvs),
+ hideEnvironmentScope: false,
isLoading: false,
variables: mockVariablesWithScopes(projectString),
};
@@ -56,6 +57,7 @@ describe('Ci variable table', () => {
expect(findCiVariableModal().props()).toEqual({
areScopedVariablesAvailable: defaultProps.areScopedVariablesAvailable,
environments: defaultProps.environments,
+ hideEnvironmentScope: defaultProps.hideEnvironmentScope,
variables: defaultProps.variables,
mode: ADD_VARIABLE_ACTION,
selectedVariable: {},
diff --git a/spec/frontend/ci_variable_list/components/ci_variable_shared_spec.js b/spec/frontend/ci_variable_list/components/ci_variable_shared_spec.js
index 78c0bd2aa1f..0cc0ee7a9c7 100644
--- a/spec/frontend/ci_variable_list/components/ci_variable_shared_spec.js
+++ b/spec/frontend/ci_variable_list/components/ci_variable_shared_spec.js
@@ -48,6 +48,7 @@ const mockProvide = {
const defaultProps = {
areScopedVariablesAvailable: true,
+ hideEnvironmentScope: false,
refetchAfterMutation: false,
};
@@ -318,6 +319,7 @@ describe('Ci Variable Shared Component', () => {
expect(findCiSettings().props()).toEqual({
areScopedVariablesAvailable: wrapper.props().areScopedVariablesAvailable,
+ hideEnvironmentScope: defaultProps.hideEnvironmentScope,
isLoading: false,
variables: wrapper.props().queryData.ciVariables.lookup(mockVariablesValue.data)?.nodes,
environments: expectedEnvironments,
diff --git a/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb b/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb
index 61fb0d9458b..aab21776a99 100644
--- a/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb
+++ b/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Create do
let_it_be(:project) { create(:project) }
let_it_be(:incident) { create(:incident, project: project) }
let_it_be(:timeline_event_tag) do
- create(:incident_management_timeline_event_tag, project: project)
+ create(:incident_management_timeline_event_tag, project: project, name: 'Test tag 1')
end
let(:args) { { note: 'note', occurred_at: Time.current } }
@@ -54,6 +54,92 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Create do
it_behaves_like 'creating an incident timeline event'
end
+
+ context 'when predefined tags are passed' do
+ let(:args) do
+ {
+ note: 'note',
+ occurred_at: Time.current,
+ timeline_event_tag_names: ['Start time']
+ }
+ end
+
+ it_behaves_like 'creating an incident timeline event'
+
+ it 'creates and sets the tag on the event' do
+ timeline_event = resolve[:timeline_event]
+
+ expect(timeline_event.timeline_event_tags.by_names(['Start time']).count).to eq 1
+ end
+ end
+
+ context 'when predefined tags exist' do
+ let_it_be(:end_time_tag) do
+ create(:incident_management_timeline_event_tag, project: project, name: 'End time')
+ end
+
+ let(:args) do
+ {
+ note: 'note',
+ occurred_at: Time.current,
+ timeline_event_tag_names: ['End time']
+ }
+ end
+
+ it 'does not create a new tag' do
+ expect { resolve }.not_to change(IncidentManagement::TimelineEventTag, :count)
+ end
+ end
+
+ context 'when same tags are tried to be assigned to same timeline event' do
+ let(:args) do
+ {
+ note: 'note',
+ occurred_at: Time.current,
+ timeline_event_tag_names: ['Start time', 'Start time']
+ }
+ end
+
+ it 'only assigns the tag once on the event' do
+ timeline_event = resolve[:timeline_event]
+
+ expect(timeline_event.timeline_event_tags.by_names(['Start time']).count).to eq(1)
+ expect(timeline_event.timeline_event_tags.count).to eq(1)
+ end
+ end
+
+ context 'with case-insentive tags' do
+ let(:args) do
+ {
+ note: 'note',
+ occurred_at: Time.current,
+ timeline_event_tag_names: ['tESt tAg 1']
+ }
+ end
+
+ it 'sets the tag on the event' do
+ timeline_event = resolve[:timeline_event]
+
+ expect(timeline_event.timeline_event_tags.by_names(['Test tag 1']).count).to eq(1)
+ end
+ end
+
+ context 'when non-existing tags are passed' do
+ let(:args) do
+ {
+ note: 'note',
+ occurred_at: Time.current,
+ timeline_event_tag_names: ['other time']
+ }
+ end
+
+ it_behaves_like 'responding with an incident timeline errors',
+ errors: ["Following tags don't exist: [\"other time\"]"]
+
+ it 'does not create the timeline event' do
+ expect { resolve }.not_to change(IncidentManagement::TimelineEvent, :count)
+ end
+ end
end
it_behaves_like 'failing to create an incident timeline event'
diff --git a/spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb b/spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb
index dd54ac0c6ac..41dce5d76dd 100644
--- a/spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb
@@ -190,6 +190,23 @@ RSpec.describe Gitlab::GitalyClient::WithFeatureFlagActors do
)
expect(result).to be(call_result)
end
+
+ context 'when call without repository_actor' do
+ before do
+ allow(service).to receive(:repository_actor).and_return(nil)
+ allow(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception).and_call_original
+ end
+
+ it 'calls error tracking track_and_raise_for_dev_exception' do
+ expect do
+ service.gitaly_client_call(call_arg_1, call_arg_2, karg: call_arg_3)
+ end.to raise_error /gitaly_client_call called without setting repository_actor/
+
+ expect(Gitlab::ErrorTracking).to have_received(:track_and_raise_for_dev_exception).with(
+ be_a(Feature::InvalidFeatureFlagError)
+ )
+ end
+ end
end
context 'when actors_aware_gitaly_calls not enabled' do
@@ -206,5 +223,53 @@ RSpec.describe Gitlab::GitalyClient::WithFeatureFlagActors do
expect(result).to be(call_result)
end
end
+
+ describe '#gitaly_feature_flag_actors' do
+ let_it_be(:project) { create(:project) }
+ let(:repository_actor) { project.repository }
+
+ context 'when actors_aware_gitaly_calls flag is enabled' do
+ let(:user_actor) { instance_double(::User) }
+ let(:project_actor) { instance_double(Project) }
+ let(:group_actor) { instance_double(Group) }
+
+ before do
+ stub_feature_flags(actors_aware_gitaly_calls: true)
+
+ allow(Feature::Gitaly).to receive(:user_actor).and_return(user_actor)
+ allow(Feature::Gitaly).to receive(:project_actor).with(project).and_return(project_actor)
+ allow(Feature::Gitaly).to receive(:group_actor).with(project).and_return(group_actor)
+ end
+
+ it 'returns a hash with collected feature flag actors' do
+ result = service.gitaly_feature_flag_actors(repository_actor)
+ expect(result).to eql(
+ repository: repository_actor,
+ user: user_actor,
+ project: project_actor,
+ group: group_actor
+ )
+
+ expect(Feature::Gitaly).to have_received(:user_actor).with(no_args)
+ expect(Feature::Gitaly).to have_received(:project_actor).with(project)
+ expect(Feature::Gitaly).to have_received(:group_actor).with(project)
+ end
+ end
+
+ context 'when actors_aware_gitaly_calls not enabled' do
+ before do
+ stub_feature_flags(actors_aware_gitaly_calls: false)
+ end
+
+ it 'returns an empty hash' do
+ expect(Feature::Gitaly).not_to receive(:user_actor)
+ expect(Feature::Gitaly).not_to receive(:project_actor)
+ expect(Feature::Gitaly).not_to receive(:group_actor)
+
+ result = service.gitaly_feature_flag_actors(repository_actor)
+ expect(result).to eql({})
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/import_export/import_test_coverage_spec.rb b/spec/lib/gitlab/import_export/import_test_coverage_spec.rb
index 51c0008b2b4..b1f5574fba1 100644
--- a/spec/lib/gitlab/import_export/import_test_coverage_spec.rb
+++ b/spec/lib/gitlab/import_export/import_test_coverage_spec.rb
@@ -96,11 +96,11 @@ RSpec.describe 'Test coverage of the Project Import' do
case item
when Hash
item.each do |k, v|
- if (v.is_a?(Array) || v.is_a?(Hash)) && v.present?
- new_path = path + [k]
- res << new_path
- gather_relations(v, res, new_path)
- end
+ next unless (v.is_a?(Array) || v.is_a?(Hash)) && v.present?
+
+ new_path = path + [k]
+ res << new_path
+ gather_relations(v, res, new_path)
end
when Array
item.each { |i| gather_relations(i, res, path) }
diff --git a/spec/lib/gitlab/instrumentation/redis_cluster_validator_spec.rb b/spec/lib/gitlab/instrumentation/redis_cluster_validator_spec.rb
index e4af3f77d5d..58c75bff9dd 100644
--- a/spec/lib/gitlab/instrumentation/redis_cluster_validator_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_cluster_validator_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe Gitlab::Instrumentation::RedisClusterValidator do
it do
stub_rails_env(env)
- args = [:mget, 'foo', 'bar']
+ args = [[:mget, 'foo', 'bar']]
if should_raise
expect { described_class.validate!(args) }
@@ -58,7 +58,7 @@ RSpec.describe Gitlab::Instrumentation::RedisClusterValidator do
with_them do
it do
- args = [command] + arguments
+ args = [[command] + arguments]
if should_raise
expect { described_class.validate!(args) }
@@ -68,13 +68,32 @@ RSpec.describe Gitlab::Instrumentation::RedisClusterValidator do
end
end
end
+
+ where(:arguments, :should_raise) do
+ [[:get, "foo"], [:get, "bar"]] | true
+ [[:get, "foo"], [:mget, "foo", "bar"]] | true # mix of single-key and multi-key cmds
+ [[:get, "{foo}:name"], [:get, "{foo}:profile"]] | false
+ [[:del, "foo"], [:del, "bar"]] | true
+ [] | false # pipeline or transaction opened and closed without ops
+ end
+
+ with_them do
+ it do
+ if should_raise
+ expect { described_class.validate!(arguments) }
+ .to raise_error(described_class::CrossSlotError)
+ else
+ expect { described_class.validate!(arguments) }.not_to raise_error
+ end
+ end
+ end
end
describe '.allow_cross_slot_commands' do
it 'does not raise for invalid arguments' do
expect do
described_class.allow_cross_slot_commands do
- described_class.validate!([:mget, 'foo', 'bar'])
+ described_class.validate!([[:mget, 'foo', 'bar']])
end
end.not_to raise_error
end
@@ -83,10 +102,10 @@ RSpec.describe Gitlab::Instrumentation::RedisClusterValidator do
expect do
described_class.allow_cross_slot_commands do
described_class.allow_cross_slot_commands do
- described_class.validate!([:mget, 'foo', 'bar'])
+ described_class.validate!([[:mget, 'foo', 'bar']])
end
- described_class.validate!([:mget, 'foo', 'bar'])
+ described_class.validate!([[:mget, 'foo', 'bar']])
end
end.not_to raise_error
end
diff --git a/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb b/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
index 5b5516f100b..02c5dfb7521 100644
--- a/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
@@ -57,8 +57,8 @@ RSpec.describe Gitlab::Instrumentation::RedisInterceptor, :clean_gitlab_redis_sh
Gitlab::Redis::SharedState.with do |redis|
redis.pipelined do |pipeline|
- pipeline.call(:get, 'foobar')
- pipeline.call(:get, 'foobarbaz')
+ pipeline.call(:get, '{foobar}buz')
+ pipeline.call(:get, '{foobar}baz')
end
end
end
@@ -103,11 +103,22 @@ RSpec.describe Gitlab::Instrumentation::RedisInterceptor, :clean_gitlab_redis_sh
Gitlab::Redis::SharedState.with do |redis|
redis.pipelined do |pipeline|
- pipeline.call(:get, 'foobar')
- pipeline.call(:get, 'foobarbaz')
+ pipeline.call(:get, '{foobar}:buz')
+ pipeline.call(:get, '{foobar}baz')
end
end
end
+
+ it 'raises error when keys are not from the same slot' do
+ expect do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.pipelined do |pipeline|
+ pipeline.call(:get, 'foo')
+ pipeline.call(:get, 'bar')
+ end
+ end
+ end.to raise_error(instance_of(Gitlab::Instrumentation::RedisClusterValidator::CrossSlotError))
+ end
end
end
diff --git a/spec/lib/gitlab/redis/multi_store_spec.rb b/spec/lib/gitlab/redis/multi_store_spec.rb
index 6e3001b38b7..207fe28e84e 100644
--- a/spec/lib/gitlab/redis/multi_store_spec.rb
+++ b/spec/lib/gitlab/redis/multi_store_spec.rb
@@ -127,17 +127,15 @@ RSpec.describe Gitlab::Redis::MultiStore do
end
before(:all) do
- primary_store.multi do |multi|
- multi.set(key1, value1)
- multi.set(key2, value2)
- multi.sadd(skey, [value1, value2])
- end
+ primary_store.set(key1, value1)
+ primary_store.set(key2, value2)
+ primary_store.sadd?(skey, value1)
+ primary_store.sadd?(skey, value2)
- secondary_store.multi do |multi|
- multi.set(key1, value1)
- multi.set(key2, value2)
- multi.sadd(skey, [value1, value2])
- end
+ secondary_store.set(key1, value1)
+ secondary_store.set(key2, value2)
+ secondary_store.sadd?(skey, value1)
+ secondary_store.sadd?(skey, value2)
end
RSpec.shared_examples_for 'reads correct value' do
@@ -349,15 +347,11 @@ RSpec.describe Gitlab::Redis::MultiStore do
primary_store.flushdb
secondary_store.flushdb
- primary_store.multi do |multi|
- multi.set(key2, value1)
- multi.sadd?(skey, value1)
- end
+ primary_store.set(key2, value1)
+ primary_store.sadd?(skey, value1)
- secondary_store.multi do |multi|
- multi.set(key2, value1)
- multi.sadd?(skey, value1)
- end
+ secondary_store.set(key2, value1)
+ secondary_store.sadd?(skey, value1)
end
with_them do
diff --git a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
index d0b935d59dd..08c712889a8 100644
--- a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
@@ -17,6 +17,7 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
# Without freezing the time, the test may behave inconsistently
# depending on which day of the week test is run.
# Monday 6th of June
+ described_class.clear_memoization(:known_events)
reference_time = Time.utc(2020, 6, 1)
travel_to(reference_time) { example.run }
described_class.clear_memoization(:known_events)
diff --git a/spec/models/incident_management/timeline_event_tag_spec.rb b/spec/models/incident_management/timeline_event_tag_spec.rb
index 3d3ce08a909..1ec4fa30fb5 100644
--- a/spec/models/incident_management/timeline_event_tag_spec.rb
+++ b/spec/models/incident_management/timeline_event_tag_spec.rb
@@ -55,5 +55,9 @@ RSpec.describe IncidentManagement::TimelineEventTag do
expect(project2.incident_management_timeline_event_tags.by_names(['Test tag 1',
'Test tag 3'])).to contain_exactly(tag3)
end
+
+ it 'returns one matching tag with case insensitive' do
+ expect(described_class.by_names(['tESt tAg 2'])).to contain_exactly(tag2)
+ end
end
end
diff --git a/spec/models/instance_metadata_spec.rb b/spec/models/instance_metadata_spec.rb
index 5fc073c392d..46fd165e065 100644
--- a/spec/models/instance_metadata_spec.rb
+++ b/spec/models/instance_metadata_spec.rb
@@ -9,7 +9,8 @@ RSpec.describe InstanceMetadata do
expect(subject).to have_attributes(
version: Gitlab::VERSION,
revision: Gitlab.revision,
- kas: kind_of(::InstanceMetadata::Kas)
+ kas: kind_of(::InstanceMetadata::Kas),
+ enterprise: Gitlab.ee?
)
end
end
diff --git a/spec/presenters/packages/npm/package_presenter_spec.rb b/spec/presenters/packages/npm/package_presenter_spec.rb
index 8b99e6d8605..4fa469c7cd2 100644
--- a/spec/presenters/packages/npm/package_presenter_spec.rb
+++ b/spec/presenters/packages/npm/package_presenter_spec.rb
@@ -86,10 +86,10 @@ RSpec.describe ::Packages::Npm::PackagePresenter do
it 'avoids N+1 database queries' do
check_n_plus_one(:versions) do
create_list(:npm_package, 5, project: project, name: package_name).each do |npm_package|
- if has_dependencies
- ::Packages::DependencyLink.dependency_types.keys.each do |dependency_type|
- create(:packages_dependency_link, package: npm_package, dependency_type: dependency_type)
- end
+ next unless has_dependencies
+
+ ::Packages::DependencyLink.dependency_types.keys.each do |dependency_type|
+ create(:packages_dependency_link, package: npm_package, dependency_type: dependency_type)
end
end
end
diff --git a/spec/requests/api/graphql/metadata_query_spec.rb b/spec/requests/api/graphql/metadata_query_spec.rb
index 840bd7c018c..435e1b5b596 100644
--- a/spec/requests/api/graphql/metadata_query_spec.rb
+++ b/spec/requests/api/graphql/metadata_query_spec.rb
@@ -17,7 +17,8 @@ RSpec.describe 'getting project information' do
'enabled' => Gitlab::Kas.enabled?,
'version' => expected_kas_version,
'externalUrl' => expected_kas_external_url
- }
+ },
+ 'enterprise' => Gitlab.ee?
}
}
end
diff --git a/spec/requests/jira_connect/cors_preflight_checks_controller_spec.rb b/spec/requests/jira_connect/cors_preflight_checks_controller_spec.rb
index aeea77091b1..d441a8575d0 100644
--- a/spec/requests/jira_connect/cors_preflight_checks_controller_spec.rb
+++ b/spec/requests/jira_connect/cors_preflight_checks_controller_spec.rb
@@ -50,13 +50,6 @@ RSpec.describe JiraConnect::CorsPreflightChecksController do
it_behaves_like 'allows cross-origin requests on self managed'
end
- describe 'OPTIONS /-/jira_connect/subscriptions' do
- let(:allowed_methods) { 'GET, POST, OPTIONS' }
- let(:path) { '/-/jira_connect/subscriptions' }
-
- it_behaves_like 'allows cross-origin requests on self managed'
- end
-
describe 'OPTIONS /-/jira_connect/subscriptions/:id' do
let(:allowed_methods) { 'DELETE, OPTIONS' }
let(:path) { '/-/jira_connect/subscriptions/123' }
diff --git a/spec/requests/jira_connect/subscriptions_controller_spec.rb b/spec/requests/jira_connect/subscriptions_controller_spec.rb
index 2a0b73d4ab1..b5f3ab916a4 100644
--- a/spec/requests/jira_connect/subscriptions_controller_spec.rb
+++ b/spec/requests/jira_connect/subscriptions_controller_spec.rb
@@ -28,14 +28,6 @@ RSpec.describe JiraConnect::SubscriptionsController do
it { is_expected.to include('http://self-managed-gitlab.com/api/') }
it { is_expected.to include('http://self-managed-gitlab.com/oauth/') }
- it 'allows cross-origin requests', :aggregate_failures do
- get path, params: params, headers: cors_request_headers
-
- expect(response.headers['Access-Control-Allow-Origin']).to eq 'https://gitlab.com'
- expect(response.headers['Access-Control-Allow-Methods']).to eq 'GET, POST, OPTIONS'
- expect(response.headers['Access-Control-Allow-Credentials']).to be_nil
- end
-
context 'with no self-managed instance configured' do
let_it_be(:installation) { create(:jira_connect_installation, instance_url: '') }
@@ -55,34 +47,6 @@ RSpec.describe JiraConnect::SubscriptionsController do
end
end
- describe 'POST /-/jira_connect/subscriptions' do
- let_it_be(:installation) { create(:jira_connect_installation, instance_url: 'http://self-managed-gitlab.com') }
- let_it_be(:group) { create(:group) }
- let_it_be(:user) { create(:user) }
-
- let(:qsh) do
- Atlassian::Jwt.create_query_string_hash('https://gitlab.test/subscriptions', 'GET', 'https://gitlab.test')
- end
-
- let(:jwt) { Atlassian::Jwt.encode({ iss: installation.client_key, qsh: qsh }, installation.shared_secret) }
- let(:cors_request_headers) { { 'Origin' => 'http://notgitlab.com' } }
- let(:params) { { jwt: jwt, namespace_path: group.path, format: :json } }
-
- before do
- group.add_maintainer(user)
- sign_in(user)
- stub_application_setting(jira_connect_proxy_url: 'https://gitlab.com')
- end
-
- it 'allows cross-origin requests', :aggregate_failures do
- post '/-/jira_connect/subscriptions', params: params, headers: cors_request_headers
-
- expect(response.headers['Access-Control-Allow-Origin']).to eq 'https://gitlab.com'
- expect(response.headers['Access-Control-Allow-Methods']).to eq 'GET, POST, OPTIONS'
- expect(response.headers['Access-Control-Allow-Credentials']).to be_nil
- end
- end
-
describe 'DELETE /-/jira_connect/subscriptions/:id' do
let_it_be(:installation) { create(:jira_connect_installation, instance_url: 'http://self-managed-gitlab.com') }
let_it_be(:subscription) { create(:jira_connect_subscription, installation: installation) }
diff --git a/spec/services/incident_management/timeline_events/create_service_spec.rb b/spec/services/incident_management/timeline_events/create_service_spec.rb
index 877cd41bf83..b10862a78b5 100644
--- a/spec/services/incident_management/timeline_events/create_service_spec.rb
+++ b/spec/services/incident_management/timeline_events/create_service_spec.rb
@@ -154,6 +154,48 @@ RSpec.describe IncidentManagement::TimelineEvents::CreateService do
result = execute.payload[:timeline_event]
expect(result.timeline_event_tags.first).to eq(timeline_event_tag)
end
+
+ context 'when predefined tags are passed' do
+ let(:args) do
+ {
+ note: 'note',
+ occurred_at: Time.current,
+ action: 'new comment',
+ promoted_from_note: comment,
+ timeline_event_tag_names: ['start time', 'end time']
+ }
+ end
+
+ it_behaves_like 'success response'
+
+ it 'matches the two tags on the event and creates on project' do
+ result = execute.payload[:timeline_event]
+
+ expect(result.timeline_event_tags.count).to eq(2)
+ expect(result.timeline_event_tags.by_names(['Start time', 'End time']).pluck_names)
+ .to match_array(['Start time', 'End time'])
+ expect(project.incident_management_timeline_event_tags.pluck_names)
+ .to include('Start time', 'End time')
+ end
+ end
+
+ context 'when invalid tag names are passed' do
+ let(:args) do
+ {
+ note: 'note',
+ occurred_at: Time.current,
+ action: 'new comment',
+ promoted_from_note: comment,
+ timeline_event_tag_names: ['some other time']
+ }
+ end
+
+ it_behaves_like 'error response', "Following tags don't exist: [\"some other time\"]"
+
+ it 'does not create timeline event' do
+ expect { execute }.not_to change(IncidentManagement::TimelineEvent, :count)
+ end
+ end
end
context 'with editable param' do
diff --git a/spec/tooling/lib/tooling/helm3_client_spec.rb b/spec/tooling/lib/tooling/helm3_client_spec.rb
index 41c51ec5754..52d1b5a1567 100644
--- a/spec/tooling/lib/tooling/helm3_client_spec.rb
+++ b/spec/tooling/lib/tooling/helm3_client_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe Tooling::Helm3Client do
describe '#releases' do
it 'raises an error if the Helm command fails' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(helm list --namespace "#{namespace}" --max 256 --offset 0 --output json)])
+ .with([%(helm list --max 256 --offset 0 --output json)])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
expect { subject.releases.to_a }.to raise_error(described_class::CommandFailedError)
@@ -43,7 +43,7 @@ RSpec.describe Tooling::Helm3Client do
it 'calls helm list with default arguments' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(helm list --namespace "#{namespace}" --max 256 --offset 0 --output json)])
+ .with([%(helm list --max 256 --offset 0 --output json)])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
subject.releases.to_a
@@ -51,7 +51,7 @@ RSpec.describe Tooling::Helm3Client do
it 'calls helm list with extra arguments' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(helm list --namespace "#{namespace}" --max 256 --offset 0 --output json --deployed)])
+ .with([%(helm list --max 256 --offset 0 --output json --deployed)])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
subject.releases(args: ['--deployed']).to_a
@@ -59,7 +59,7 @@ RSpec.describe Tooling::Helm3Client do
it 'returns a list of Release objects' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(helm list --namespace "#{namespace}" --max 256 --offset 0 --output json --deployed)])
+ .with([%(helm list --max 256 --offset 0 --output json --deployed)])
.and_return(Gitlab::Popen::Result.new([], raw_helm_list_page2, '', double(success?: true)))
expect(Gitlab::Popen).to receive(:popen_with_detail).ordered
.and_return(Gitlab::Popen::Result.new([], raw_helm_list_empty, '', double(success?: true)))
@@ -80,13 +80,13 @@ RSpec.describe Tooling::Helm3Client do
it 'automatically paginates releases' do
expect(Gitlab::Popen).to receive(:popen_with_detail).ordered
- .with([%(helm list --namespace "#{namespace}" --max 256 --offset 0 --output json)])
+ .with([%(helm list --max 256 --offset 0 --output json)])
.and_return(Gitlab::Popen::Result.new([], raw_helm_list_page1, '', double(success?: true)))
expect(Gitlab::Popen).to receive(:popen_with_detail).ordered
- .with([%(helm list --namespace "#{namespace}" --max 256 --offset 256 --output json)])
+ .with([%(helm list --max 256 --offset 256 --output json)])
.and_return(Gitlab::Popen::Result.new([], raw_helm_list_page2, '', double(success?: true)))
expect(Gitlab::Popen).to receive(:popen_with_detail).ordered
- .with([%(helm list --namespace "#{namespace}" --max 256 --offset 512 --output json)])
+ .with([%(helm list --max 256 --offset 512 --output json)])
.and_return(Gitlab::Popen::Result.new([], raw_helm_list_empty, '', double(success?: true)))
releases = subject.releases.to_a
@@ -98,7 +98,7 @@ RSpec.describe Tooling::Helm3Client do
describe '#delete' do
it 'raises an error if the Helm command fails' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(helm uninstall --namespace "#{namespace}" #{release_name})])
+ .with([%(helm uninstall #{release_name})])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
expect { subject.delete(release_name: release_name) }.to raise_error(described_class::CommandFailedError)
@@ -106,7 +106,7 @@ RSpec.describe Tooling::Helm3Client do
it 'calls helm uninstall with default arguments' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(helm uninstall --namespace "#{namespace}" #{release_name})])
+ .with([%(helm uninstall #{release_name})])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
expect(subject.delete(release_name: release_name)).to eq('')
@@ -117,16 +117,16 @@ RSpec.describe Tooling::Helm3Client do
it 'raises an error if the Helm command fails' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(helm uninstall --namespace "#{namespace}" #{release_name.join(' ')})])
- .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
+ .with([%(helm uninstall #{release_name.join(' ')})])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
expect { subject.delete(release_name: release_name) }.to raise_error(described_class::CommandFailedError)
end
it 'calls helm uninstall with multiple release names' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(helm uninstall --namespace "#{namespace}" #{release_name.join(' ')})])
- .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
+ .with([%(helm uninstall #{release_name.join(' ')})])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
expect(subject.delete(release_name: release_name)).to eq('')
end
diff --git a/spec/views/devise/confirmations/almost_there.html.haml_spec.rb b/spec/views/devise/confirmations/almost_there.html.haml_spec.rb
new file mode 100644
index 00000000000..c091efe9295
--- /dev/null
+++ b/spec/views/devise/confirmations/almost_there.html.haml_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'devise/confirmations/almost_there' do
+ describe 'confirmations text' do
+ subject { render(template: 'devise/confirmations/almost_there') }
+
+ before do
+ allow(view).to receive(:params).and_return(email: email)
+ end
+
+ context 'when correct email' do
+ let(:email) { 'こんにちは@test' }
+
+ specify do
+ subject
+
+ expect(rendered).to have_content(
+ "Please check your email (#{email}) to confirm your account"
+ )
+ end
+ end
+
+ context 'when random text' do
+ let(:email) { 'random text' }
+
+ specify do
+ subject
+
+ expect(rendered).to have_content(
+ 'Please check your email to confirm your account'
+ )
+ end
+ end
+ end
+end
diff --git a/tooling/lib/tooling/helm3_client.rb b/tooling/lib/tooling/helm3_client.rb
index 82ebe3f51dc..d83dbeac76b 100644
--- a/tooling/lib/tooling/helm3_client.rb
+++ b/tooling/lib/tooling/helm3_client.rb
@@ -37,7 +37,6 @@ module Tooling
def delete(release_name:)
run_command([
'uninstall',
- %(--namespace "#{namespace}"),
release_name
])
end
@@ -60,7 +59,6 @@ module Tooling
def raw_releases(page, args = [])
command = [
'list',
- %(--namespace "#{namespace}"),
%(--max #{PAGINATION_SIZE}),
%(--offset #{PAGINATION_SIZE * page}),
%(--output json),
diff --git a/yarn.lock b/yarn.lock
index 3e1e1b88281..1a2f200dd15 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1108,19 +1108,19 @@
stylelint-declaration-strict-value "1.8.0"
stylelint-scss "4.2.0"
-"@gitlab/svgs@3.7.0":
- version "3.7.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.7.0.tgz#1257b69fb9898ea5614f992aa6b6dc3619c3c38c"
- integrity sha512-6vTqWZzY63ZUTUqk0dmMDcfU27qtkAu0WmlK4e3FMWmISvTxNhAk2j11c/YlLauf6okE4W2T2fnhvXp1mzcPgA==
+"@gitlab/svgs@3.8.0":
+ version "3.8.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.8.0.tgz#bc7fa51e345e26cff56fdff629ea439adfa1e0cb"
+ integrity sha512-DUWeG2Vx+1ntZ/1GT6S36ZOtXvM5Wm02MtDRrQS4GuOX4rkTeG9aoutSJuwQ2h9BNtxl0U/jkf5GVBxacj18XA==
-"@gitlab/ui@49.3.0":
- version "49.3.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-49.3.0.tgz#63e6a375d66c6f6ae568f0d1a08fe0e9bd4e355b"
- integrity sha512-c8GSajEdW2Q1ME7lYuQgImR493WaELKJOq/T+1zVs3i82cc1YDWbGEJyKZh6srJ6xNSLuIbn6d7oSqfM/jeSAQ==
+"@gitlab/ui@49.10.0":
+ version "49.10.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-49.10.0.tgz#54715c18d3e06f313b572c5b9b807622f7a35b19"
+ integrity sha512-hBkU5TIdc2bzqe4P2X/BZXgQQQa+Sp4A5eWiKK+FVuCx5l1To2q4EyHHPMn292AJx7Y23qM2jP4GMphbs2wtDg==
dependencies:
"@popperjs/core" "^2.11.2"
bootstrap-vue "2.20.1"
- dompurify "^2.4.0"
+ dompurify "^2.4.1"
echarts "^5.3.2"
iframe-resizer "^4.3.2"
lodash "^4.17.20"
@@ -4992,7 +4992,7 @@ dompurify@2.3.8:
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.8.tgz#224fe9ae57d7ebd9a1ae1ac18c1c1ca3f532226f"
integrity sha512-eVhaWoVibIzqdGYjwsBWodIQIaXFSB+cKDf4cfxLMsK0xiud6SE+/WCVx/Xw/UwQsa4cS3T2eITcdtmTg2UKcw==
-dompurify@^2.4.0, dompurify@^2.4.1:
+dompurify@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.1.tgz#f9cb1a275fde9af6f2d0a2644ef648dd6847b631"
integrity sha512-ewwFzHzrrneRjxzmK6oVz/rZn9VWspGFRDb4/rRtIsM1n36t9AKma/ye8syCpcw+XJ25kOK/hOG7t1j2I2yBqA==