From f18d1ffec0ecaae592a0ccd708ce77146f5f37e3 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 8 Nov 2021 18:09:52 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../components/added_commit_message.vue | 6 +- .../source_branch_removal_status.vue | 2 +- .../states/mr_widget_auto_merge_enabled.vue | 4 +- .../components/states/mr_widget_merging.vue | 2 +- .../components/states/ready_to_merge.vue | 4 +- app/helpers/application_settings_helper.rb | 4 + app/models/application_setting.rb | 12 ++ .../application_settings/_sentry.html.haml | 22 +++ .../metrics_and_profiling.html.haml | 12 ++ bin/sidekiq-cluster | 8 +- ...nfigure_sentry_in_application_settings.yml | 8 + ...sentry_settings_to_application_settings.rb | 12 ++ ...sentry_settings_on_application_settings.rb | 17 ++ ...5194425_schedule_requirements_migration.rb | 35 ++++ db/schema_migrations/20211005194425 | 1 + db/schema_migrations/20211021125908 | 1 + db/schema_migrations/20211021134458 | 1 + db/structure.sql | 7 + doc/api/packages/maven.md | 14 +- .../avoiding_downtime_in_migrations.md | 176 +++++++++++++++++- doc/development/migration_style_guide.md | 1 + doc/integration/jira/index.md | 34 ++-- doc/user/admin_area/index.md | 4 + doc/user/clusters/agent/repository.md | 2 +- doc/user/clusters/management_project.md | 6 +- .../clusters/management_project_template.md | 85 ++++++--- doc/user/group/saml_sso/scim_setup.md | 51 +++-- .../packages/composer_repository/index.md | 2 +- .../img/import_export_download_export.png | Bin 14867 -> 23285 bytes .../img/import_export_export_button.png | Bin 14530 -> 31790 bytes doc/user/project/settings/import_export.md | 2 + .../migrate_requirements_to_work_items.rb | 13 ++ locale/gitlab.pot | 71 ++++--- .../login/maintain_log_in_mixed_env_spec.rb | 2 +- .../cli.rb | 24 +-- sidekiq_cluster/dependencies.rb | 6 + .../sidekiq_cluster.rb | 17 +- .../sidekiq_cluster/cli_spec.rb | 12 +- .../concerns/renders_commits_spec.rb | 6 + ...user_merges_when_pipeline_succeeds_spec.rb | 4 +- .../user_sees_merge_widget_spec.rb | 2 +- .../mr_widget_auto_merge_enabled_spec.js.snap | 4 +- .../mr_widget_auto_merge_enabled_spec.js | 4 +- .../states/mr_widget_merging_spec.js | 2 +- .../vue_mr_widget/mr_widget_options_spec.js | 4 +- spec/requests/api/settings_spec.rb | 41 ++++ .../sidekiq_cluster_spec.rb | 5 +- spec/support/flaky_tests.rb | 2 +- spec/tooling/quality/test_level_spec.rb | 8 +- tooling/quality/test_level.rb | 1 + 50 files changed, 601 insertions(+), 162 deletions(-) create mode 100644 app/views/admin/application_settings/_sentry.html.haml create mode 100644 config/feature_flags/development/configure_sentry_in_application_settings.yml create mode 100644 db/migrate/20211021125908_add_sentry_settings_to_application_settings.rb create mode 100644 db/migrate/20211021134458_add_limits_to_sentry_settings_on_application_settings.rb create mode 100644 db/post_migrate/20211005194425_schedule_requirements_migration.rb create mode 100644 db/schema_migrations/20211005194425 create mode 100644 db/schema_migrations/20211021125908 create mode 100644 db/schema_migrations/20211021134458 create mode 100644 lib/gitlab/background_migration/migrate_requirements_to_work_items.rb rename {lib/gitlab/sidekiq_cluster => sidekiq_cluster}/cli.rb (92%) create mode 100644 sidekiq_cluster/dependencies.rb rename {lib/gitlab => sidekiq_cluster}/sidekiq_cluster.rb (89%) rename spec/{lib/gitlab => commands}/sidekiq_cluster/cli_spec.rb (96%) rename spec/{lib/gitlab => sidekiq_cluster}/sidekiq_cluster_spec.rb (98%) diff --git a/app/assets/javascripts/vue_merge_request_widget/components/added_commit_message.vue b/app/assets/javascripts/vue_merge_request_widget/components/added_commit_message.vue index fc17669a737..492e68b636f 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/added_commit_message.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/added_commit_message.vue @@ -40,9 +40,9 @@ export default { }, message() { return this.isFastForwardEnabled - ? s__('mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}.') + ? s__('mrWidgetCommitsAdded|Adds %{commitCount} to %{targetBranch}.') : s__( - 'mrWidgetCommitsAdded|%{commitCount} and %{mergeCommitCount} will be added to %{targetBranch}%{squashedCommits}.', + 'mrWidgetCommitsAdded|Adds %{commitCount} and %{mergeCommitCount} to %{targetBranch}%{squashedCommits}.', ); }, textDecorativeComponent() { @@ -69,7 +69,7 @@ export default { diff --git a/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue b/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue index 9268e426954..caafd6b995e 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue @@ -4,7 +4,7 @@ import { __ } from '../../locale'; export default { i18n: { - removesBranchText: __('The source branch will be deleted'), + removesBranchText: __('Deletes the source branch'), tooltipTitle: __('A user with write access to the source branch selected this option'), }, components: { diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue index 0eb173edbcb..a44caf886a4 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue @@ -177,10 +177,10 @@ export default {

- {{ s__('mrWidget|The source branch will be deleted') }} + {{ s__('mrWidget|Deletes the source branch') }}

- {{ s__('mrWidget|The source branch will not be deleted') }} + {{ s__('mrWidget|Does not delete the source branch') }}

- {{ s__('mrWidget|The changes will be merged into') }} + {{ s__('mrWidget|Merges changes into') }} {{ mr.targetBranch }} diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue index 3ca0c387478..aca838ce099 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue @@ -710,10 +710,10 @@ export default {

  • diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 2103a37180f..b8ee71daeee 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -404,6 +404,10 @@ module ApplicationSettingsHelper :keep_latest_artifact, :whats_new_variant, :user_deactivation_emails_enabled, + :sentry_enabled, + :sentry_dsn, + :sentry_clientside_dsn, + :sentry_environment, :sidekiq_job_limiter_mode, :sidekiq_job_limiter_compression_threshold_bytes, :sidekiq_job_limiter_limit_bytes, diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 5a8cbd8d71c..af5796d682f 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -536,6 +536,18 @@ class ApplicationSetting < ApplicationRecord validates :sidekiq_job_limiter_limit_bytes, numericality: { only_integer: true, greater_than_or_equal_to: 0 } + validates :sentry_enabled, + inclusion: { in: [true, false], message: _('must be a boolean value') } + validates :sentry_dsn, + addressable_url: true, presence: true, length: { maximum: 255 }, + if: :sentry_enabled? + validates :sentry_clientside_dsn, + addressable_url: true, allow_blank: true, length: { maximum: 255 }, + if: :sentry_enabled? + validates :sentry_environment, + presence: true, length: { maximum: 255 }, + if: :sentry_enabled? + attr_encrypted :asset_proxy_secret_key, mode: :per_attribute_iv, key: Settings.attr_encrypted_db_key_base_truncated, diff --git a/app/views/admin/application_settings/_sentry.html.haml b/app/views/admin/application_settings/_sentry.html.haml new file mode 100644 index 00000000000..5fd373d59e9 --- /dev/null +++ b/app/views/admin/application_settings/_sentry.html.haml @@ -0,0 +1,22 @@ += form_for @application_setting, url: metrics_and_profiling_admin_application_settings_path(anchor: 'js-sentry-settings'), html: { class: 'fieldset-form', id: 'sentry-settings' } do |f| + = form_errors(@application_setting) + + %span.text-muted + = _('Changing any setting here requires an application restart') + + %fieldset + .form-group + .form-check + = f.check_box :sentry_enabled, class: 'form-check-input' + = f.label :sentry_enabled, _('Enable Sentry error tracking'), class: 'form-check-label' + .form-group + = f.label :sentry_dsn, _('DSN'), class: 'label-light' + = f.text_field :sentry_dsn, class: 'form-control gl-form-input', placeholder: 'https://public@sentry.example.com/1' + .form-group + = f.label :sentry_clientside_dsn, _('Clientside DSN'), class: 'label-light' + = f.text_field :sentry_clientside_dsn, class: 'form-control gl-form-input', placeholder: 'https://public@sentry.example.com/2' + .form-group + = f.label :sentry_environment, _('Environment'), class: 'label-light' + = f.text_field :sentry_environment, class: 'form-control gl-form-input', placeholder: Rails.env + + = f.submit _('Save changes'), class: 'gl-button btn btn-confirm' diff --git a/app/views/admin/application_settings/metrics_and_profiling.html.haml b/app/views/admin/application_settings/metrics_and_profiling.html.haml index 6087551d7c7..eba50b93bfe 100644 --- a/app/views/admin/application_settings/metrics_and_profiling.html.haml +++ b/app/views/admin/application_settings/metrics_and_profiling.html.haml @@ -54,3 +54,15 @@ = render 'usage' = render_if_exists 'admin/application_settings/pseudonymizer_settings', expanded: expanded_by_default? + +- if Feature.enabled?(:configure_sentry_in_application_settings, default_enabled: :yaml) + %section.settings.as-sentry.no-animate#js-sentry-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'sentry_settings_content' } } + .settings-header + %h4 + = _('Sentry') + %button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' } + = expanded_by_default? ? _('Collapse') : _('Expand') + %p + = _('Configure Sentry integration for error tracking') + .settings-content + = render 'sentry' diff --git a/bin/sidekiq-cluster b/bin/sidekiq-cluster index 47f8e82d228..db7833acdc9 100755 --- a/bin/sidekiq-cluster +++ b/bin/sidekiq-cluster @@ -1,13 +1,7 @@ #!/usr/bin/env ruby # frozen_string_literal: true -require 'optparse' -require_relative '../lib/gitlab' -require_relative '../lib/gitlab/utils' -require_relative '../lib/gitlab/sidekiq_config/cli_methods' -require_relative '../lib/gitlab/sidekiq_config/worker_matcher' -require_relative '../lib/gitlab/sidekiq_cluster' -require_relative '../lib/gitlab/sidekiq_cluster/cli' +require_relative '../sidekiq_cluster/cli' Thread.abort_on_exception = true diff --git a/config/feature_flags/development/configure_sentry_in_application_settings.yml b/config/feature_flags/development/configure_sentry_in_application_settings.yml new file mode 100644 index 00000000000..82b2261994b --- /dev/null +++ b/config/feature_flags/development/configure_sentry_in_application_settings.yml @@ -0,0 +1,8 @@ +--- +name: configure_sentry_in_application_settings +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73381 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/344832 +milestone: '14.5' +type: development +group: group::pipeline execution +default_enabled: false diff --git a/db/migrate/20211021125908_add_sentry_settings_to_application_settings.rb b/db/migrate/20211021125908_add_sentry_settings_to_application_settings.rb new file mode 100644 index 00000000000..d8b40893b47 --- /dev/null +++ b/db/migrate/20211021125908_add_sentry_settings_to_application_settings.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class AddSentrySettingsToApplicationSettings < Gitlab::Database::Migration[1.0] + # rubocop:disable Migration/AddLimitToTextColumns + def change + add_column :application_settings, :sentry_enabled, :boolean, default: false, null: false + add_column :application_settings, :sentry_dsn, :text + add_column :application_settings, :sentry_clientside_dsn, :text + add_column :application_settings, :sentry_environment, :text + end + # rubocop:enable Migration/AddLimitToTextColumns +end diff --git a/db/migrate/20211021134458_add_limits_to_sentry_settings_on_application_settings.rb b/db/migrate/20211021134458_add_limits_to_sentry_settings_on_application_settings.rb new file mode 100644 index 00000000000..34d18741788 --- /dev/null +++ b/db/migrate/20211021134458_add_limits_to_sentry_settings_on_application_settings.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddLimitsToSentrySettingsOnApplicationSettings < Gitlab::Database::Migration[1.0] + disable_ddl_transaction! + + def up + add_text_limit :application_settings, :sentry_dsn, 255 + add_text_limit :application_settings, :sentry_clientside_dsn, 255 + add_text_limit :application_settings, :sentry_environment, 255 + end + + def down + remove_text_limit :application_settings, :sentry_dsn + remove_text_limit :application_settings, :sentry_clientside_dsn + remove_text_limit :application_settings, :sentry_environment + end +end diff --git a/db/post_migrate/20211005194425_schedule_requirements_migration.rb b/db/post_migrate/20211005194425_schedule_requirements_migration.rb new file mode 100644 index 00000000000..56211989b8e --- /dev/null +++ b/db/post_migrate/20211005194425_schedule_requirements_migration.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +class ScheduleRequirementsMigration < Gitlab::Database::Migration[1.0] + DOWNTIME = false + + # 2021-10-05 requirements count: ~12500 + # + # Using 30 as batch size and 120 seconds default interval will produce: + # ~420 jobs - taking ~14 hours to perform + BATCH_SIZE = 30 + + MIGRATION = 'MigrateRequirementsToWorkItems' + + disable_ddl_transaction! + + class Requirement < ActiveRecord::Base + include EachBatch + + self.table_name = 'requirements' + end + + def up + queue_background_migration_jobs_by_range_at_intervals( + Requirement.where(issue_id: nil), + MIGRATION, + 2.minutes, + batch_size: BATCH_SIZE, + track_jobs: true + ) + end + + def down + # NO OP + end +end diff --git a/db/schema_migrations/20211005194425 b/db/schema_migrations/20211005194425 new file mode 100644 index 00000000000..cd3710a6492 --- /dev/null +++ b/db/schema_migrations/20211005194425 @@ -0,0 +1 @@ +6647e94d315c76629f9726e26bafd124fb2fed361568d65315e7c7557f8d9ecf \ No newline at end of file diff --git a/db/schema_migrations/20211021125908 b/db/schema_migrations/20211021125908 new file mode 100644 index 00000000000..9cb92e1eabe --- /dev/null +++ b/db/schema_migrations/20211021125908 @@ -0,0 +1 @@ +d6fbe3efc3e45b750d82e277e30b7b0048b960d9f9f5b4f7c6a7a1ed869e76b5 \ No newline at end of file diff --git a/db/schema_migrations/20211021134458 b/db/schema_migrations/20211021134458 new file mode 100644 index 00000000000..dc168e12229 --- /dev/null +++ b/db/schema_migrations/20211021134458 @@ -0,0 +1 @@ +1baa8db0d42a8d99e48b61930f5c42d1af5f86555488419b6551e1dbf417d3ad \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 531e969b413..588061cdd3f 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -10457,6 +10457,10 @@ CREATE TABLE application_settings ( encrypted_content_validation_api_key bytea, encrypted_content_validation_api_key_iv bytea, content_validation_endpoint_enabled boolean DEFAULT false NOT NULL, + sentry_enabled boolean DEFAULT false NOT NULL, + sentry_dsn text, + sentry_clientside_dsn text, + sentry_environment text, CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)), CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)), CONSTRAINT app_settings_ext_pipeline_validation_service_url_text_limit CHECK ((char_length(external_pipeline_validation_service_url) <= 255)), @@ -10465,9 +10469,12 @@ CREATE TABLE application_settings ( CONSTRAINT app_settings_yaml_max_size_positive CHECK ((max_yaml_size_bytes > 0)), CONSTRAINT check_17d9558205 CHECK ((char_length((kroki_url)::text) <= 1024)), CONSTRAINT check_2dba05b802 CHECK ((char_length(gitpod_url) <= 255)), + CONSTRAINT check_3def0f1829 CHECK ((char_length(sentry_clientside_dsn) <= 255)), + CONSTRAINT check_4f8b811780 CHECK ((char_length(sentry_dsn) <= 255)), CONSTRAINT check_51700b31b5 CHECK ((char_length(default_branch_name) <= 255)), CONSTRAINT check_57123c9593 CHECK ((char_length(help_page_documentation_base_url) <= 255)), CONSTRAINT check_5a84c3ffdc CHECK ((char_length(content_validation_endpoint_url) <= 255)), + CONSTRAINT check_5bcba483c4 CHECK ((char_length(sentry_environment) <= 255)), CONSTRAINT check_718b4458ae CHECK ((char_length(personal_access_token_prefix) <= 20)), CONSTRAINT check_7227fad848 CHECK ((char_length(rate_limiting_response_text) <= 255)), CONSTRAINT check_85a39b68ff CHECK ((char_length(encrypted_ci_jwt_signing_key_iv) <= 255)), diff --git a/doc/api/packages/maven.md b/doc/api/packages/maven.md index b4b3d579ffb..b046b0dc411 100644 --- a/doc/api/packages/maven.md +++ b/doc/api/packages/maven.md @@ -36,13 +36,13 @@ GET packages/maven/*path/:file_name | `file_name` | string | yes | The name of the Maven package file. | ```shell -curl --header "Private-Token: " "https://gitlab.example.com/api/v4/packages/maven/foo/bar/baz/mypkg-1.0-SNAPSHOT.jar" +curl --header "Private-Token: " "https://gitlab.example.com/api/v4/packages/maven/foo/bar/mypkg/1.0-SNAPSHOT/mypkg-1.0-SNAPSHOT.jar" ``` To write the output to file: ```shell -curl --header "Private-Token: " "https://gitlab.example.com/api/v4/packages/maven/foo/bar/baz/mypkg-1.0-SNAPSHOT.jar" >> mypkg-1.0-SNAPSHOT.jar +curl --header "Private-Token: " "https://gitlab.example.com/api/v4/packages/maven/foo/bar/mypkg/1.0-SNAPSHOT/mypkg-1.0-SNAPSHOT.jar" >> mypkg-1.0-SNAPSHOT.jar ``` This writes the downloaded file to `mypkg-1.0-SNAPSHOT.jar` in the current directory. @@ -63,13 +63,13 @@ GET groups/:id/-/packages/maven/*path/:file_name | `file_name` | string | yes | The name of the Maven package file. | ```shell -curl --header "Private-Token: " "https://gitlab.example.com/api/v4/groups/1/-/packages/maven/foo/bar/baz/mypkg-1.0-SNAPSHOT.jar" +curl --header "Private-Token: " "https://gitlab.example.com/api/v4/groups/1/-/packages/maven/foo/bar/mypkg/1.0-SNAPSHOT/mypkg-1.0-SNAPSHOT.jar" ``` To write the output to file: ```shell -curl --header "Private-Token: " "https://gitlab.example.com/api/v4/groups/1/-/packages/maven/foo/bar/baz/mypkg-1.0-SNAPSHOT.jar" >> mypkg-1.0-SNAPSHOT.jar +curl --header "Private-Token: " "https://gitlab.example.com/api/v4/groups/1/-/packages/maven/foo/bar/mypkg/1.0-SNAPSHOT/mypkg-1.0-SNAPSHOT.jar" >> mypkg-1.0-SNAPSHOT.jar ``` This writes the downloaded file to `mypkg-1.0-SNAPSHOT.jar` in the current directory. @@ -90,13 +90,13 @@ GET projects/:id/packages/maven/*path/:file_name | `file_name` | string | yes | The name of the Maven package file. | ```shell -curl --header "Private-Token: " "https://gitlab.example.com/api/v4/projects/1/packages/maven/foo/bar/baz/mypkg-1.0-SNAPSHOT.jar" +curl --header "Private-Token: " "https://gitlab.example.com/api/v4/projects/1/packages/maven/foo/bar/mypkg/1.0-SNAPSHOT/mypkg-1.0-SNAPSHOT.jar" ``` To write the output to file: ```shell -curl --header "Private-Token: " "https://gitlab.example.com/api/v4/projects/1/packages/maven/foo/bar/baz/mypkg-1.0-SNAPSHOT.jar" >> mypkg-1.0-SNAPSHOT.jar +curl --header "Private-Token: " "https://gitlab.example.com/api/v4/projects/1/packages/maven/foo/bar/mypkg/1.0-SNAPSHOT/mypkg-1.0-SNAPSHOT.jar" >> mypkg-1.0-SNAPSHOT.jar ``` This writes the downloaded file to `mypkg-1.0-SNAPSHOT.jar` in the current directory. @@ -120,5 +120,5 @@ PUT projects/:id/packages/maven/*path/:file_name curl --request PUT \ --upload-file path/to/mypkg-1.0-SNAPSHOT.pom \ --header "Private-Token: " \ - "https://gitlab.example.com/api/v4/projects/1/packages/maven/foo/bar/baz/mypkg-1.0-SNAPSHOT.pom" + "https://gitlab.example.com/api/v4/projects/1/packages/maven/foo/bar/mypkg/1.0-SNAPSHOT/mypkg-1.0-SNAPSHOT.pom" ``` diff --git a/doc/development/avoiding_downtime_in_migrations.md b/doc/development/avoiding_downtime_in_migrations.md index 9418eafa487..a5fc1909551 100644 --- a/doc/development/avoiding_downtime_in_migrations.md +++ b/doc/development/avoiding_downtime_in_migrations.md @@ -377,7 +377,181 @@ ensures that no downtime is needed. This operation does not require downtime. -## Data Migrations +## Migrating `integer` primary keys to `bigint` + +To [prevent the overflow risk](https://gitlab.com/groups/gitlab-org/-/epics/4785) for some tables +with `integer` primary key (PK), we have to migrate their PK to `bigint`. The process to do this +without downtime and causing too much load on the database is described below. + +### Initialize the conversion and start migrating existing data (release N) + +To start the process, add a regular migration to create the new `bigint` columns. Use the provided +`initialize_conversion_of_integer_to_bigint` helper. The helper also creates a database trigger +to keep in sync both columns for any new records ([see an example](https://gitlab.com/gitlab-org/gitlab/-/blob/41fbe34a4725a4e357a83fda66afb382828767b2/db/migrate/20210608072312_initialize_conversion_of_ci_stages_to_bigint.rb)): + +```ruby +class InitializeConversionOfCiStagesToBigint < ActiveRecord::Migration[6.1] + include Gitlab::Database::MigrationHelpers + + TABLE = :ci_stages + COLUMNS = %i(id) + + def up + initialize_conversion_of_integer_to_bigint(TABLE, COLUMNS) + end + + def down + revert_initialize_conversion_of_integer_to_bigint(TABLE, COLUMNS) + end +end +``` + +Ignore the new `bigint` columns: + +```ruby +module Ci + class Stage < Ci::ApplicationRecord + include IgnorableColumns + ignore_column :id_convert_to_bigint, remove_with: '14.2', remove_after: '2021-08-22' + end +``` + +To migrate existing data, we introduced new type of _batched background migrations_. +Unlike the classic background migrations, built on top of Sidekiq, batched background migrations +don't have to enqueue and schedule all the background jobs at the beginning. +They also have other advantages, like automatic tuning of the batch size, better progress visibility, +and collecting metrics. To start the process, use the provided `backfill_conversion_of_integer_to_bigint` +helper ([example](https://gitlab.com/gitlab-org/gitlab/-/blob/41fbe34a4725a4e357a83fda66afb382828767b2/db/migrate/20210608072346_backfill_ci_stages_for_bigint_conversion.rb)): + +```ruby +class BackfillCiStagesForBigintConversion < ActiveRecord::Migration[6.1] + include Gitlab::Database::MigrationHelpers + + TABLE = :ci_stages + COLUMNS = %i(id) + + def up + backfill_conversion_of_integer_to_bigint(TABLE, COLUMNS) + end + + def down + revert_backfill_conversion_of_integer_to_bigint(TABLE, COLUMNS) + end +end +``` + +### Monitor the background migration + +Check how the migration is performing while it's running. Multiple ways to do this are described below. + +#### High-level status of batched background migrations + +See how to [check the status of batched background migrations](../update/index.md#checking-for-background-migrations-before-upgrading). + +#### Query the database + +We can query the related database tables directly. Requires access to read-only replica. +Example queries: + +```sql +-- Get details for batched background migration for given table +SELECT * FROM batched_background_migrations WHERE table_name = 'namespaces'\gx + +-- Get count of batched background migration jobs by status for given table +SELECT + batched_background_migrations.id, batched_background_migration_jobs.status, COUNT(*) +FROM + batched_background_migrations + JOIN batched_background_migration_jobs ON batched_background_migrations.id = batched_background_migration_jobs.batched_background_migration_id +WHERE + table_name = 'namespaces' +GROUP BY + batched_background_migrations.id, batched_background_migration_jobs.status; + +-- Batched background migration progress for given table (based on estimated total number of tuples) +SELECT + m.table_name, + LEAST(100 * sum(j.batch_size) / pg_class.reltuples, 100) AS percentage_complete +FROM + batched_background_migrations m + JOIN batched_background_migration_jobs j ON j.batched_background_migration_id = m.id + JOIN pg_class ON pg_class.relname = m.table_name +WHERE + j.status = 3 AND m.table_name = 'namespaces' +GROUP BY m.id, pg_class.reltuples; +``` + +#### Sidekiq logs + +We can also use the Sidekiq logs to monitor the worker that executes the batched background +migrations: + +1. Sign in to [Kibana](https://log.gprd.gitlab.net) with a `@gitlab.com` email address. +1. Change the index pattern to `pubsub-sidekiq-inf-gprd*`. +1. Add filter for `json.queue: cronjob:database_batched_background_migration`. + +#### PostgerSQL slow queries log + +Slow queries log keeps track of low queries that took above 1 second to execute. To see them +for batched background migration: + +1. Sign in to [Kibana](https://log.gprd.gitlab.net) with a `@gitlab.com` email address. +1. Change the index pattern to `pubsub-postgres-inf-gprd*`. +1. Add filter for `json.endpoint_id.keyword: Database::BatchedBackgroundMigrationWorker`. +1. Optional. To see only updates, add a filter for `json.command_tag.keyword: UPDATE`. +1. Optional. To see only failed statements, add a filter for `json.error_severiry.keyword: ERROR`. +1. Optional. Add a filter by table name. + +#### Grafana dashboards + +To monitor the health of the database, use these additional metrics: + +- [PostgreSQL Tuple Statistics](https://dashboards.gitlab.net/d/000000167/postgresql-tuple-statistics?orgId=1&refresh=1m): if you see high rate of updates for the tables being actively converted, or increasing percentage of dead tuples for this table, it might mean that autovacuum cannot keep up. +- [PostgreSQL Overview](https://dashboards.gitlab.net/d/000000144/postgresql-overview?orgId=1): if you see high system usage or transactions per second (TPS) on the primary database server, it might mean that the migration is causing problems. + +### Prometheus metrics + +Number of [metrics](https://gitlab.com/gitlab-org/gitlab/-/blob/294a92484ce4611f660439aa48eee4dfec2230b5/lib/gitlab/database/background_migration/batched_migration_wrapper.rb#L90-128) +for each batched background migration are published to Prometheus. These metrics can be searched for and +visualized in Thanos ([see an example](https://thanos-query.ops.gitlab.net/graph?g0.expr=sum%20(rate(batched_migration_job_updated_tuples_total%7Benv%3D%22gprd%22%7D%5B5m%5D))%20by%20(migration_id)%20&g0.tab=0&g0.stacked=0&g0.range_input=3d&g0.max_source_resolution=0s&g0.deduplicate=1&g0.partial_response=0&g0.store_matches=%5B%5D&g0.end_input=2021-06-13%2012%3A18%3A24&g0.moment_input=2021-06-13%2012%3A18%3A24)). + +### Swap the columns (release N + 1) + +After the background is completed and the new `bigint` columns are populated for all records, we can +swap the columns. Swapping is done with post-deployment migration. The exact process depends on the +table being converted, but in general it's done in the following steps: + +1. Using the provided `ensure_batched_background_migration_is_finished` helper, make sure the batched +migration has finished ([see an example](https://gitlab.com/gitlab-org/gitlab/-/blob/41fbe34a4725a4e357a83fda66afb382828767b2/db/post_migrate/20210707210916_finalize_ci_stages_bigint_conversion.rb#L13-18)). +If the migration has not completed, the subsequent steps fail anyway. By checking in advance we +aim to have more helpful error message. +1. Create indexes using the `bigint` columns that match the existing indexes using the `integer` +column ([see an example](https://gitlab.com/gitlab-org/gitlab/-/blob/41fbe34a4725a4e357a83fda66afb382828767b2/db/post_migrate/20210707210916_finalize_ci_stages_bigint_conversion.rb#L28-34)). +1. Create foreign keys (FK) using the `bigint` columns that match the existing FKs using the +`integer` column. Do this both for FK referencing other tables, and FKs that reference the table +that is being migrated ([see an example](https://gitlab.com/gitlab-org/gitlab/-/blob/41fbe34a4725a4e357a83fda66afb382828767b2/db/post_migrate/20210707210916_finalize_ci_stages_bigint_conversion.rb#L36-43)). +1. Inside a transaction, swap the columns: + 1. Lock the tables involved. To reduce the chance of hitting a deadlock, we recommended to do this in parent to child order ([see an example](https://gitlab.com/gitlab-org/gitlab/-/blob/41fbe34a4725a4e357a83fda66afb382828767b2/db/post_migrate/20210707210916_finalize_ci_stages_bigint_conversion.rb#L47)). + 1. Rename the columns to swap names ([see an example](https://gitlab.com/gitlab-org/gitlab/-/blob/41fbe34a4725a4e357a83fda66afb382828767b2/db/post_migrate/20210707210916_finalize_ci_stages_bigint_conversion.rb#L49-54)) + 1. Reset the trigger function ([see an example](https://gitlab.com/gitlab-org/gitlab/-/blob/41fbe34a4725a4e357a83fda66afb382828767b2/db/post_migrate/20210707210916_finalize_ci_stages_bigint_conversion.rb#L56-57)). + 1. Swap the defaults ([see an example](https://gitlab.com/gitlab-org/gitlab/-/blob/41fbe34a4725a4e357a83fda66afb382828767b2/db/post_migrate/20210707210916_finalize_ci_stages_bigint_conversion.rb#L59-62)). + 1. Swap the PK constraint (if any) ([see an example](https://gitlab.com/gitlab-org/gitlab/-/blob/41fbe34a4725a4e357a83fda66afb382828767b2/db/post_migrate/20210707210916_finalize_ci_stages_bigint_conversion.rb#L64-68)). + 1. Remove old indexes and rename new ones ([see an example](https://gitlab.com/gitlab-org/gitlab/-/blob/41fbe34a4725a4e357a83fda66afb382828767b2/db/post_migrate/20210707210916_finalize_ci_stages_bigint_conversion.rb#L70-72)). + 1. Remove old FKs (if still present) and rename new ones ([see an example](https://gitlab.com/gitlab-org/gitlab/-/blob/41fbe34a4725a4e357a83fda66afb382828767b2/db/post_migrate/20210707210916_finalize_ci_stages_bigint_conversion.rb#L74)). + +See example [merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66088), and [migration](https://gitlab.com/gitlab-org/gitlab/-/blob/41fbe34a4725a4e357a83fda66afb382828767b2/db/post_migrate/20210707210916_finalize_ci_stages_bigint_conversion.rb). + +### Remove the trigger and old `integer` columns (release N + 2) + +Using post-deployment migration and the provided `cleanup_conversion_of_integer_to_bigint` helper, +drop the database trigger and the old `integer` columns ([see an example](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69714)). + +### Remove ignore rules (release N + 3) + +In the next release after the columns were dropped, remove the ignore rules as we do not need them +anymore ([see an example](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71161)). + +## Data migrations Data migrations can be tricky. The usual approach to migrate data is to take a 3 step approach: diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md index e03b96a0e14..d249d36f159 100644 --- a/doc/development/migration_style_guide.md +++ b/doc/development/migration_style_guide.md @@ -135,6 +135,7 @@ various database operations, such as: - [dropping and renaming columns](avoiding_downtime_in_migrations.md#dropping-columns) - [changing column constraints and types](avoiding_downtime_in_migrations.md#changing-column-constraints) - [adding and dropping indexes, tables, and foreign keys](avoiding_downtime_in_migrations.md#adding-indexes) +- [migrating `integer` primary keys to `bigint`](avoiding_downtime_in_migrations.md#adding-indexes) and explains how to perform them without requiring downtime. diff --git a/doc/integration/jira/index.md b/doc/integration/jira/index.md index e3214ec26fe..3052d85b2cb 100644 --- a/doc/integration/jira/index.md +++ b/doc/integration/jira/index.md @@ -10,7 +10,7 @@ If your organization uses [Jira](https://www.atlassian.com/software/jira) issues you can [migrate your issues from Jira](../../user/project/import/jira.md) and work exclusively in GitLab. However, if you'd like to continue to use Jira, you can integrate it with GitLab. GitLab offers two types of Jira integrations, and you -can use one or both depending on the capabilities you need. It is recommended that you enable both. +can use one or both depending on the capabilities you need. We recommend you enable both. ## Compare integrations @@ -41,7 +41,7 @@ or the Jira DVCS (distributed version control system) connector, ### Direct feature comparison -| Capability | Jira integration | Jira Development panel integration | +| Capability | Jira integration | Jira development panel integration | |-|-|-| | Mention a Jira issue ID in a GitLab commit or merge request, and a link to the Jira issue is created. | Yes. | No. | | Mention a Jira issue ID in GitLab and the Jira issue shows the GitLab issue or merge request. | Yes. A Jira comment with the GitLab issue or MR title links to GitLab. The first mention is also added to the Jira issue under **Web links**. | Yes, in the issue's [development panel](https://support.atlassian.com/jira-software-cloud/docs/view-development-information-for-an-issue/). | @@ -55,11 +55,11 @@ or the Jira DVCS (distributed version control system) connector, ## Authentication in Jira -The process for configuring Jira depends on whether you host Jira on your own server or on +The authentication method in Jira depends on whether you host Jira on your own server or on [Atlassian cloud](https://www.atlassian.com/cloud): - **Jira Server** supports basic authentication. When connecting, a **username and password** are - required. Connecting to Jira Server via CAS is not possible. For more information, read + required. Connecting to Jira Server using the Central Authentication Service (CAS) is not possible. For more information, read how to [set up a user in Jira Server](jira_server_configuration.md). - **Jira on Atlassian cloud** supports authentication through an API token. When connecting to Jira on Atlassian cloud, an email and API token are required. For more information, read @@ -72,11 +72,16 @@ actions in GitLab issues and merge requests linked to a Jira issue leak informat about the private project to non-administrator Jira users. If your installation uses Jira Cloud, you can use the [GitLab.com for Jira Cloud app](connect-app.md) to avoid this risk. +## Third-party Jira integrations + +Developers have built several third-party Jira integrations for GitLab that are +listed on the [Atlassian Marketplace](https://marketplace.atlassian.com/search?product=jira&query=gitlab). + ## Troubleshooting If these features do not work as expected, it is likely due to a problem with the way the integration settings were configured. -### GitLab is unable to comment on a Jira issue +### GitLab cannot comment on a Jira issue If GitLab cannot comment on Jira issues, make sure the Jira user you set up for the integration has permission to: @@ -86,14 +91,16 @@ set up for the integration has permission to: Jira issue references and update comments do not work if the GitLab issue tracker is disabled. -### GitLab is unable to close a Jira issue +### GitLab cannot close a Jira issue -Make sure the `Transition ID` you set in the Jira settings matches the one -your project needs to close an issue. +If GitLab cannot close a Jira issue: -Make sure that the Jira issue is not already marked as resolved. That is, -the Jira issue resolution field is not set, and the issue is not struck through in -Jira lists. +- Make sure the `Transition ID` you set in the Jira settings matches the one + your project needs to close an issue. + +- Make sure the Jira issue is not already marked as resolved: + - Check the Jira issue resolution field is not set. + - Check the issue is not struck through in Jira lists. ### CAPTCHA @@ -104,8 +111,3 @@ authenticate with the Jira site. To fix this error, sign in to your Jira instance and complete the CAPTCHA. - -## Third-party Jira integrations - -Developers have built several third-party Jira integrations for GitLab that are -listed on the [Atlassian Marketplace](https://marketplace.atlassian.com/search?product=jira&query=gitlab). diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md index 27d2bd8f764..a0f1d9fcb67 100644 --- a/doc/user/admin_area/index.md +++ b/doc/user/admin_area/index.md @@ -257,6 +257,10 @@ To edit a topic, select **Edit** in that topic's row. To search for topics by name, enter your criteria in the search box. The topic search is case insensitive, and applies partial matching. +NOTE: +Topics are public and visible to everyone, but assignments to projects are not. +Do not include sensitive information in the name or description of a topic. + ### Administering Jobs You can administer all jobs in the GitLab instance from the Admin Area's Jobs page. diff --git a/doc/user/clusters/agent/repository.md b/doc/user/clusters/agent/repository.md index 4dda2fc4498..182b03d0c9f 100644 --- a/doc/user/clusters/agent/repository.md +++ b/doc/user/clusters/agent/repository.md @@ -173,7 +173,7 @@ To grant projects access to the Agent through the [CI/CD Tunnel](ci_cd_tunnel.md 1. Go to your Agent's configuration project. 1. Edit the Agent's configuration file (`config.yaml`). 1. Add the `projects` attribute into `ci_access`. -1. Identify the new project through its path: +1. Identify the project through its path: ```yaml ci_access: diff --git a/doc/user/clusters/management_project.md b/doc/user/clusters/management_project.md index e9cbe43d7fb..7bfaacd96a3 100644 --- a/doc/user/clusters/management_project.md +++ b/doc/user/clusters/management_project.md @@ -12,6 +12,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w WARNING: This feature was [deprecated](https://gitlab.com/groups/gitlab-org/configure/-/epics/8) in GitLab 14.5. +To manage cluster applications, use the [GitLab Kubernetes Agent](agent/index.md) +with the [Cluster Management Project Template](management_project_template.md). + A project can be designated as the management project for a cluster. A management project can be used to run deployment jobs with Kubernetes @@ -41,8 +44,7 @@ Management projects are restricted to the following: To use a cluster management project to manage your cluster: 1. Create a new project to serve as the cluster management project -for your cluster. We recommend that you -[create this project based on the Cluster Management project template](management_project_template.md#create-a-new-project-based-on-the-cluster-management-template). +for your cluster. 1. [Associate the cluster with the management project](#associate-the-cluster-management-project-with-the-cluster). 1. [Configure your cluster's pipelines](#configuring-your-pipeline). 1. [Set the environment scope](#setting-the-environment-scope). diff --git a/doc/user/clusters/management_project_template.md b/doc/user/clusters/management_project_template.md index ea364063758..c663246cdd8 100644 --- a/doc/user/clusters/management_project_template.md +++ b/doc/user/clusters/management_project_template.md @@ -4,15 +4,17 @@ group: Configure info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- -# Cluster Management project template **(FREE)** +# Manage cluster applications **(FREE)** > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25318) in GitLab 12.10 with Helmfile support via Helm v2. > - Helm v2 support was [dropped](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63577) in GitLab 14.0. Use Helm v3 instead. +> - [Migrated](https://gitlab.com/gitlab-org/project-templates/cluster-management/-/merge_requests/24) to the GitLab Kubernetes Agent in GitLab 14.5. -With a [cluster management project](management_project.md) you can manage -your cluster's deployment and applications through a repository in GitLab. +Use a repository to install, manage, and deploy clusters applications through code. -The Cluster Management project template provides you a baseline to get +## Cluster Management Project Template + +The Cluster Management Project Template provides you a baseline to get started and flexibility to customize your project to your cluster's needs. For instance, you can: @@ -21,49 +23,78 @@ For instance, you can: - Remove the built-in cluster applications you don't need. - Add other cluster applications using the same structure as the ones already available. -The template contains the following [components](#available-components): +The template contains the following [components](#configure-the-available-components): -- A pre-configured GitLab CI/CD file so that you can configure deployment pipelines. +- A pre-configured GitLab CI/CD file so that you can configure CI/CD pipelines using the [CI/CD Tunnel](agent/ci_cd_tunnel.md). - A pre-configured [Helmfile](https://github.com/roboll/helmfile) so that you can manage cluster applications with [Helm v3](https://helm.sh/). - An `applications` directory with a `helmfile.yaml` configured for each application available in the template. -WARNING: -If you used [GitLab Managed Apps](applications.md) to manage your -cluster from GitLab, see how to [migrate from GitLab Managed Apps](migrating_from_gma_to_project_template.md) to the Cluster Management -project. +## Use the Kubernetes Agent with the Cluster Management Project Template -## Set up the management project from the Cluster Management project template +To use a new project created from the Cluster Management Project Template +with a cluster connected to GitLab through the [GitLab Kubernetes Agent](agent/index.md), +you have two options: -To set up your cluster's management project off of the Cluster Management project template: +- [Use one single project](#single-project) to configure the Agent and manage cluster applications. +- [Use separate projects](#separate-projects) - one to configure the Agent and another to manage cluster applications. -1. [Create a new project based on the Cluster Management template](#create-a-new-project-based-on-the-cluster-management-template). -1. [Associate the cluster management project with your cluster](management_project.md#associate-the-cluster-management-project-with-the-cluster). -1. Use the [available components](#available-components) to manage your cluster. +### Single project -### Create a new project based on the Cluster Management template +This setup is particularly useful when you haven't connected your cluster +to GitLab through the Agent yet and you want to use the Cluster Management +Project Template to manage cluster applications. + +To use one single project to configure the Agent and to manage cluster applications: + +1. [Create a new project from the Cluster Management Project Template](#create-a-new-project-based-on-the-cluster-management-template). +1. Configure the new project as the [Agent's configuration repository](agent/repository.md) +(where the Agent is registered and its `config.yaml` is stored). +1. From your project's settings, add a [new environment variable](../../ci/variables/index.md#add-a-cicd-variable-to-a-project) `$KUBE_CONTEXT` and set it to `path/to/agent-configuration-project:your-agent-name`. +1. [Configure the components](#configure-the-available-components) inherited from the template. + +### Separate projects + +This setup is particularly useful **when you already have a cluster** connected +to GitLab through the Agent and want to use the Cluster Management +Project Template to manage cluster applications. + +To use one project to configure the Agent ("project A") and another project to +manage cluster applications ("project B"), follow the steps below. + +We assume that you already have a cluster connected through the Agent and +[configured through the Agent's configuration repository](agent/repository.md) +("project A"). + +1. [Create a new project from the Cluster Management Project Template](#create-a-new-project-based-on-the-cluster-management-template). +This new project is "project B". +1. In your "project A", [grant the Agent access to the new project (B) through the CI/CD Tunnel](agent/repository.md#authorize-projects-to-use-an-agent). +1. From the "project's B" settings, add a [new environment variable](../../ci/variables/index.md#add-a-cicd-variable-to-a-project) `$KUBE_CONTEXT` and set it to `path/to/agent-configuration-project:your-agent-name`. +1. In "project B", [configure the components](#configure-the-available-components) inherited from the template. + +## Create a new project based on the Cluster Management Template To get started, create a new project based on the Cluster Management project template to use as a cluster management project. -You can either create the [new project](../project/working_with_projects.md#create-a-project) -from the template or import the project from the URL. Importing -the project is useful if you are using a GitLab self-managed -instance that may not have the latest version of the template. +You can either create the new project from the template or import the +project from the URL. Importing the project is useful if you are using +a GitLab self-managed instance that may not have the latest version of +the template. -To create the new project: +To [create the new project](../project/working_with_projects.md#create-a-project): - From the template: select the **GitLab Cluster Management** project template. - Importing from the URL: use `https://gitlab.com/gitlab-org/project-templates/cluster-management.git`. -## Available components +## Configure the available components -Use the available components to configure your cluster: +Use the available components to configure your cluster applications: -- [A `.gitlab-ci.yml` file](#the-gitlab-ciyml-file). -- [A main `helmfile.yml` file](#the-main-helmfileyml-file). -- [A directory with built-in applications](#built-in-applications). +- [The `.gitlab-ci.yml` file](#the-gitlab-ciyml-file). +- [The main `helmfile.yml` file](#the-main-helmfileyml-file). +- [The directory with built-in applications](#built-in-applications). ### The `.gitlab-ci.yml` file @@ -107,7 +138,7 @@ The [built-in supported applications](https://gitlab.com/gitlab-org/project-temp - [Sentry](../infrastructure/clusters/manage/management_project_applications/sentry.md) - [Vault](../infrastructure/clusters/manage/management_project_applications/vault.md) -#### How to customize your applications +#### Customize your applications Each app has an `applications/{app}/values.yaml` file (`applications/{app}/values.yaml.gotmpl` in case of GitLab Runner). This is the place where you can define default values for your app's Helm chart. Some apps already have defaults diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md index 01260baa432..dd4558b4a3e 100644 --- a/doc/user/group/saml_sso/scim_setup.md +++ b/doc/user/group/saml_sso/scim_setup.md @@ -35,9 +35,10 @@ The following identity providers are supported: Once [Group Single Sign-On](index.md) has been configured, we can: -1. Navigate to the group and click **Administration > SAML SSO**. -1. Click on the **Generate a SCIM token** button. -1. Save the token and URL so they can be used in the next step. +1. On the top bar, select **Menu > Groups** and find your group. +1. On the left sidebar, select **Settings > SAML SSO**. +1. Select **Generate a SCIM token**. +1. Save the token and URL for use in the next step. ![SCIM token configuration](img/scim_token_v13_3.png) @@ -50,14 +51,14 @@ Once [Group Single Sign-On](index.md) has been configured, we can: The SAML application that was created during [Single sign-on](index.md) setup for [Azure](https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/view-applications-portal) now needs to be set up for SCIM. -1. Set up automatic provisioning and administrative credentials by following the +1. Enable automatic provisioning and administrative credentials by following the [Azure's SCIM setup documentation](https://docs.microsoft.com/en-us/azure/active-directory/app-provisioning/use-scim-to-provision-users-and-groups#provisioning-users-and-groups-to-applications-that-support-scim). During this configuration, note the following: -- The `Tenant URL` and `secret token` are the ones retrieved in the +- The `Tenant URL` and `secret token` are the items retrieved in the [previous step](#gitlab-configuration). -- It is recommended to set a notification email and check the **Send an email notification when a failure occurs** checkbox. +- We recommend setting a notification email and selecting the **Send an email notification when a failure occurs** checkbox. - For mappings, we only leave `Synchronize Azure Active Directory Users to AppName` enabled. `Synchronize Azure Active Directory Groups to AppName` is usually disabled. However, this does not mean Azure AD users cannot be provisioned in groups. Leaving it enabled does not break @@ -113,29 +114,27 @@ Make sure that the Okta setup matches our documentation exactly, especially the configuration. Otherwise, the Okta SCIM app may not work properly. 1. Sign in to Okta. -1. If you see an **Admin** button in the top right, click the button. This will - ensure you are in the Admin area. +1. Ensure you are in the Admin section by selecting the **Admin** button located in the top right. The admin button is not visible from the admin page. NOTE: - If you're using the Developer Console, click **Developer Console** in the top - bar and select **Classic UI**. Otherwise, you may not see the buttons described - in the following steps: + If you're using the Developer Console, select **Developer Console** in the top + bar and then select **Classic UI**. Otherwise, you may not see the buttons described in the following steps: -1. In the **Application** tab, click **Add Application**. -1. Search for **GitLab**, find and click on the 'GitLab' application. -1. On the GitLab application overview page, click **Add**. +1. In the **Application** tab, select **Add Application**. +1. Search for **GitLab**, find and select on the 'GitLab' application. +1. On the GitLab application overview page, select **Add**. 1. Under **Application Visibility** select both checkboxes. Currently the GitLab application does not support SAML authentication so the icon should not be shown to users. -1. Click **Done** to finish adding the application. -1. In the **Provisioning** tab, click **Configure API integration**. +1. Select **Done** to finish adding the application. +1. In the **Provisioning** tab, select **Configure API integration**. 1. Select **Enable API integration**. - For **Base URL** enter the URL obtained from the GitLab SCIM configuration page - For **API Token** enter the SCIM token obtained from the GitLab SCIM configuration page -1. Click 'Test API Credentials' to verify configuration. -1. Click **Save** to apply the settings. -1. After saving the API integration details, new settings tabs appear on the left. Choose **To App**. -1. Click **Edit**. -1. Check the box to **Enable** for both **Create Users** and **Deactivate Users**. -1. Click **Save**. +1. Select 'Test API Credentials' to verify configuration. +1. Select **Save** to apply the settings. +1. After saving the API integration details, new settings tabs appear on the left. Select **To App**. +1. Select **Edit**. +1. Select the **Enable** checkbox for both **Create Users** and **Deactivate Users**. +1. Select **Save**. 1. Assign users in the **Assignments** tab. Assigned users are created and managed in your GitLab group. @@ -147,8 +146,8 @@ application described above. ### OneLogin -OneLogin provides a "GitLab (SaaS)" app in their catalog, which includes a SCIM integration. -As the app is developed by OneLogin, please reach out to OneLogin if you encounter issues. +As the developers of this app, OneLogin provides a "GitLab (SaaS)" app in their catalog, which includes a SCIM integration. +Please reach out to OneLogin if you encounter issues. ## User access and linking setup @@ -177,8 +176,8 @@ As long as [Group SAML](index.md) has been configured, existing GitLab.com users - By following these steps: 1. Sign in to GitLab.com if needed. - 1. Click on the GitLab app in the identity provider's dashboard or visit the **GitLab single sign-on URL**. - 1. Click on the **Authorize** button. + 1. In the identity provider's dashboard select the GitLab app or visit the **GitLab single sign-on URL**. + 1. Select the **Authorize**. We recommend users do this prior to turning on sync, because while synchronization is active, there may be provisioning errors for existing users. diff --git a/doc/user/packages/composer_repository/index.md b/doc/user/packages/composer_repository/index.md index 6e3af45df17..1adb806b27a 100644 --- a/doc/user/packages/composer_repository/index.md +++ b/doc/user/packages/composer_repository/index.md @@ -149,7 +149,7 @@ Do not save unless you want to overwrite the existing CI/CD file. When you publish: - The same package with different data, it overwrites the existing package. -- The same package with the same data, a `404 Bad request` error occurs. +- The same package with the same data, a `400 Bad request` error occurs. ## Install a Composer package diff --git a/doc/user/project/settings/img/import_export_download_export.png b/doc/user/project/settings/img/import_export_download_export.png index c7ab7565fc7b794bde6e8d2437ae7a55c264fb36..62292e99e8ea1d657b6fa80ab2f0acdce8212b6f 100644 GIT binary patch literal 23285 zcmb^Ybx>Sg5HE_75E2L)f&>o)3+~R~5FogR;1(db%RqqO?m;KGy96h=GdP0|u7kq> zGa0yi=hV4%s?L44-m7|l?CL#x_v$s>y}EnvUcViwuBw3hn*22y8XB(RCs|E2wCBjD z0|yiR>B`sP>a!$^8ty8-Rv}j;8)u zTMmIh-2Zp%-60U&-QCv+1Q!?AInd^C6M4vx6JMIf5b5&5Pfh`R*@d|FH`E?rkxS=bvg!;RX0~zxI16LtjF{zSTn%dB5qRE7t{-3;iU^FZ%g@r&u>_ zA#CUxao+ePYvtw!E^1Tu^^<5&@AYP(IX^d#u(ak*Pojuf9^!m%c4K!eN~QdpNRqZt zteWWV&TjG6UCjw%W$!>%B`W)?=zf{?XpRTssM4d6!B!0ueC^j|0PZ4v%kV5`$_Nz1y$zHtz?M zO;1)D)&1)loW&$elj6Q>N=Zq(6;8BmT$|)Uk1wwa{Y`9yc*TWHS`Y5?9hCGuvVU1A z@ru}P?X2$(<}S5I2`dL&9xUFR?uJ;X7zNj=328~_g~3jaT|SG?&#vo4bXsOlZ%o&f zAKl*_4aW~{iwcP%9v`b#Pi_`Usyvls0Zx1EKL-!)1yrKfCg*YoR@cw&r^*95ql~|& z58Uol-Q4f~8a&*C^o`|wtH{eQp4ut_pAN0L}!OzK)p zt({9x`g=5!>kwV~K5cgm_NT35_^YYYSb53>{KQMiGotIzGdFwbX72CB`c&O$W?@id zsH;dNEI~&;x~|6AzfZd9yrwhqZ{wD)L=vQpaK%di|< zZ6ZCAArr4z{*Rdq1QAo%(G+~a!I)A+`Ix1q-_?VEeB0t6Am*pfl$3ns5}*G=gpST} zh=FXorJvmen`h)*{2u+}LG})PLH5Y6?Q!9Y7@B`Px9sy%Q{M-%cAOtg%1eDcpupRQ zqxmYk3i@uDJl_(`FtkuufqG{wYG1u{ooNyvmFJt403@o9m!?HB|Nou96Q4vsRw%0G z5Ul*DQ@D|YTiHa8HCE4tX&2;*iE!1rIpAi|+qNv6=Dv!LQsQDQg%TkxS3PZ$6qxo= zkq?Lraz8(L+(f%#b6xed(Etr`9l8DEUmUeHGZR2C2ZH|jlEPD5o{|$1=F=VtjqquG z0AC2My?KtlVCPh4V^8q0g-?mSk6-gn481w&bNa73SVc(x^OA%raySVzkfo}e6_ zVI^ouGcR+5+HaDL6HpfPQsNI`?newFT%)!1q&F-hoGK;QG6R#L zdh5t|D$ELOU?f|#uSro)|L?YVlwHd#>XbZ`i&-WqN<6~UBw&Ir zc5y%vQTH?WD{rdB`j#+0wA9aFriKtU7R|1iA9nEXpsWuPj{DW7^Pr2se=_8V`{n^; zO2;TWPda+pCcz9#9)K^UpVZMTK0_kdwX=Ztfd9A@Z`-&|)DpL1)eEuVxM*iT7Wnb= zFeaBM4dF5+pZJff_`x1uu70Wnt(Z-AC2O#dNQCvUNI1<^cW}R+@8nklqI}p>kQY!< z!_TQjXxyun)qG0)fsO0ON-*Q}I{e?q6jc43&uh6Kpx1+G;?(9shG!mb1D%fgVV1!Z zs~`#!dh;6%^sgj^`DaHI=fe2qS!W(amNdQ!N9YY66YqD)N)FK-qHVHcfIj&WYr-aG zAPIa_g(l0J-9+oy%NVULuwZ}to25gM23WO0pDye%ONEGLop<=!yXN& z%`|2an3;#Hkhgj^zJbY`S*x%023*ZbT4TR%e9jH+75I2uW!d@eytv#Nduc-*^y>ES z=*tYRyk4GH^9>Lu#^V?3SRY|4S_D<$be&xAQsbd6i(G~-1Wp+B&5D^xnVWj4@i5@^ ze;MNc3nHF_0y>Esg2ePz9N16Zf0$bwEI!(ah7S*^W2X`fCM`Z$PJ0 z4Sw?_YhQgIwuB&qCP4af(W~6Nk1LVXBTl}?0HDLKy1)YMUYin{o4{2_scRmOlS9yTIkH}sd?bIUPeZSjsp!b`Q(V=x4{6t z5-thRJFfPV+8XyI05Zz!qz+oIvS{wOcprC-4M~>(+}$L);j>hR|LF-(W^fM%3bUa< z-$$L8Ze;+Fu7)tAI2AI$b4ib~+3eBw@;uNC1oY}YdoAd5(U`=Ejau^4Z5WLWvM<1) zxHO`Be9JK%k2!;X{b`J&z4)CL2VzR={Y|YhTde04N5e;Wb6V3)X2(qSWz#F%Yz~j> zi~QGgC2{bY_G6~$_#8=+hT%-J5L!Hm*0yk-_Q?xtAU{&X4T{AP^oL&sCNrg<{)Tk| zQP3!cXqoC}w7(Q^8(m-?4VVfmqYR^CB z0Dzz2-J{LlwA>oCsjKwG7K!V&TiY(|{=`zV#b?ms7Bm_&vW6zvf@%E;XSmgA_iRt^ zc67D6HI<3;TV2bv*CcE**>8FkfrT2oJ^R3j)Xd8*bCY zcR4&Pn|A8*Mb?&qf{E^Lo>8-S{yO;F52^)oE1pJ^B4+9?a$C;A7OzyYY|%X{g<^Om zy{W%xOZVMtzdQ{#dG;ljT_nS&xS^iH+`jFjtDkG5Mq!IR?AV;-!L6UQ-pvs{Z!|(} zrnRoNGV4^ie6w)$`2g@yQEop~O7Tn6@ia`k4Sfpr`^c%W-0SND>QoJ{+S~y*APf4B zin0HTRv#OJ|efmw&5a9b6}N)JXZwu0|OO zk`9M4z{yN|wK>#}C2Fn^HqN;v@F?75LS_ZMYL)TBOXFrWBU5OQFjOZ@ap9|ibK$1_ z`vSZ70pZ=)EyyT8K54dfZ4cWx!)*QIY((}S#t`@}=y*R}X0O>G92Bm5W zCN%lC06#VC*jK$M{AYOK14BpaUgffPCUQ184Kovaj*g zbI9WDq42$g+H>|kASBgsC+bF(3yW;)IYg&UXQDur20uE2_N?JBGY)+j&&GjDA%@{{ zE6>=Jk#@M>!eDh)(5*{qyUsH)&fDAUv)a|HC+e75E4bJ6gwl-z1YkQ^z&jJm%#Mu=n z*`-+?X80<6h~9qJjv@F;gqJWkB7Ve;=pWQZ*-}&pn<||?Xs(tIHqaBa;r)w2vwcO4@r2eTsGHJWNb|^fT z7CXn41`Ku$@rW?tv`UpHj}Xz>-=)=Taou4@+T!g}jgpBfY2VPK28iD5BI^xY&LIrC+mLgR?4yo903`d`pVi)M+1{wM@i{>t6>^|Sq%Cl=Jrh@iz z-M~i1vnmZ%X@yQY!D6T4XKasMXI+c8ixYd)3A0f@eit6B{{8zG=v5#!t=Bs7bN#*EJV|`@zckpnI?QjS?!q^-aIxPA|tCGjcT2_>JL@RCAMp=&RsRjv_esA74lFSGSHAnc+IO$m-*kE*9Uz ztB1c2;@>n+t){!oCQ-QaXldpgR{@%6s(atxU|a>jXd=}2nryE>)oHx;FD4rJ z)0%3$BTMB%i|TCEbO}Aq#esRo+cHGHk6UT>0x+PK9)!1D zWZQOLci2YoLzvnX;H&BNYNI;xF55=GXLxL7R1-apgB_5(XjFZhkt@@zqY5Cwpi+Z@ zDjR7oM=nFCx<7}JooFoxzCzj^qaZEEHIpV5(N@ZnTf1-Z*w_K_f4=bNY)S+!WUZtw ztFejIT@m&JS90r2$-r2-mmBk(lTtSt$XcSW_lj!W!Sy*AsrHP-H%DsZuugqbDw_km^f%5V&`8XP zap`&cGE{SV*B4Y2g}>d&{MZK1u_>9(XgNcVKk;wp2p36knfXx~l9=|l+RxVCJj>{h zQ)X$}hsT4NdpYw?37xs--Q}Tuq&;{-f5;e+^PDj*{^ts!b;V*!aOwW-MEvpd&5Gdh zEWzG5XwtIU62;qQa(k|8)fer4`@lnCHDSPhB$*3)1`0FgOuJifPtW_+qQfnp8@J(< zv{NH%lT%kc3*-0A+=MEd5|i=}!rD*gDS}QrgMEF^9_Aco+OZN97Blv@T=%G>B4TI; zI{lF9vJlap^KnL=2hg3^cJJNlo3j`8z^)CYT6R~#_fHm2KXO%|OryHvdNMe=p(8JFmSOG< z6%j+Z)!bse*cOv{eNdu8Su^`WS4;8m7=PeZ!@Zj71;Pp_iZFfC{vNXW>%wkfp;E>h za_y*awq4fU&bu6=>o2C^CZ92Z-#)G0APo3*awE3-U8HkE7uZVO$u?~nVBDSb}GuNeyxy}{W&LQ7yQa$y%9=X(>1`2y@WA&q2TRB3ta41 zaqz7f`x8IA_)tUs`RpGO^4$TIEeV1HPtuqDmv8x>&L$TBJ2APkg)aJ^q?e*@#m}H# z*aVqp3Qhm6tH|eO_I_n9=ktoL&cyqpohL8nS>&!cVQtvdA!xn@3~6RbimkLZ3C=9b zYOE+C$i23_BSUf>-JgAHfFM~8kU9E(w>hzQE^hpe^;g{Iux&@L3g%6I$bAy!|;2$W*eK6()&vmfr0gYjtmFKXwKc8y6iu% z1e~2i5mrBWvXnFml2=)GxSG>&;)IB@q7x9RD;5 z$EZ^$_bEG&eBg|AA{sVZK?+O#D{qd}WU)I(K~UqohfYq zU6MBBhvq~s2cSwd;ZbB<@P z4;DDJAp}P_fvw+VV6of}pKnM`?5AwJucwQy7C(Kha}#MFFzq_@F4)9;=f3$sU8iDT z3rbF)V0c47ycQ}s~btIHroeD z>^r#0p=Db#>uQ5cjo?G^{UsTH(%N7QR1-gbzI|}VTb$f2iu+zh?O8xL$w$I#swS|s zrEmx(sfjqqLx&C$!g+d`Rvz@-^ib$sT#X2i6*mM!GO*13H26p=!4t% zKiYJ5?*jHZuJ3W(f3;FP=xOiCfzirzmCV*^ScUoUcF!Oa$_y(-*}L!ldvc}Nf-q(K%*XP9QqOvyL24)dcq^YY$PhNUCA8CZ9n>F%MO*0WUB1NR5{ccD z`)~sG{{SI#K~!j~ApINKRgD3O?!QgX`6lJeJ(Q4`?3bUuHi177QoEc)i!k5)P>EFw z7}gMwwzZz@x}DCTp%G_a{w^>Ba0&T^C$kx4{dbwDws1RkT&(>Yt674_=XA->MueNe z<}B}18^Hmz+3$4qeSOxxbvr$-yp21fN;R=A$xq_qyUU-rdT@7G$Dc2gp6UAK6oC`6 zWMF547VEeVECT+-PldC0d>Ks;@e!8)7ua<{O#%^kplNIHISqd`?RspI}?Ken&;ZU2_ zPQs#x=SzB#8xM}|F1|7S3HEOdJCmwukCP8-C5<)SO=~6%hCkY~8yoa!mKt3beh^1K zTAzLXQQmgS6+q)ey?sNQ-^V*@pBJbWdA}_JSdcaflHyyt(UfM0d zWf5$<>6A}1s$p>F}Mo+v66T2>DR|D8gXy>@gVTFZK#0VSL4d}rtaUAff0C&l&+*3aqqP}y-|FYg(sKI^t zQOdU7W+lctfy%$C{z6XRP&z`oee94RomH6v`KVbZ9w%Pk<>HtdohcS(uX)Hxf>h@;NOJ+OD&dc@Mwqra*o&)eMw=XiFZ@$ zl!Cf!1(~v0#gToZzm2zxjMBj|NZO7XYUL-8EHUdwR8}=ZOsngbj(jm~UaovUwhMM> zKGQFgl~C!B23!$W{t^GaCLTIUA+Vx#)nK}^o%_qM6a%LGzoyYo>0hl1G&0WoY*Lfl z#aWvoZ1;QflxCjLN*%{wJgFek%b7&-$ysHA*|(L=AoV1W?qmO5DFBLwHwG-!{QrYBkU(Ju`$J-fE$s{`ti!>TRBW{)>Tx z0f^rAYWAzCGT2IK?ibs0@7 zNmW7`k@Ati2Vg2tSo(9FELIfl*lE|9*bh}}6MC_R(JnX%L#Gt{<`%`2cyiIpfBpEk zYlpxU=U-BlZhauD)q#xx6EpH3k_GAd^Ot!fA(0G;g^AXg=*uaUe~j^UP<9E4M_1JL zECZ{`PgG&1JVfsp#c*x&TS(%Anm@dpbC1;S)Qx)YKZ%5`^Gcq3IFIy9W&>c0RCS?L zVD4k_m;ZEQ5U%#l&YDGQTQsmXZFywlkUx8BYl+HQ7qu~RSxf&`;^=0|61lR;Ix=98G`?F{r|t+sKtlV z);e+4)h7<*j2p0Gq=(dPT=rZY67M0tu_=H%-S(8~D!*_7t!JselQ zuW}t5>xg*w8K2Bfd@vtm0%RQ}r1xe@8KKODUy)}5A*%W6z z{=cvQ3Wd);PV*Y?!mdKE=YJ6Mrxm%adiRKZHUrJkxkh2%&+)KNfAY+1a{u6m^I^{# zS+wdu>$S9jDUK}MBdcf0wrRkeIj~e7XUE4PJ&-g%{ehg7SD4YQq%YVWu4<#dm773s zNE|0Y@R*bEG;e1V5|JX7n&6EBL@urN`zV@7I#r zE*JsjY5s`%Z(=LqwjNP_8(tge?%T7UV4tYy(SZ3NEG$2LF(sV=4>Rp4?}aqDavPJV zF%@Y-?RMke27?y@B1=(`M~;ng=Pe8T^n7OVpnuunRMZqq(spVr8y5YCF6wiI}13BeB)AA^(xAF~>+kf)te&y~R>eYj_ zvWLlF0H~1OYD1CCw#(A}Y=_#LbKH-X7{qn*I1PbtJ zwrMo1d@3c)`Qxok+agYcKdx+%J6QZ(m39~g;F?;}5>>80vN>RL&f1Rt+< zoO=}tbn)u&x?k|{H?Ud$wTSFAZoA!(Fj+eW{;V^3cwm(3>!Re9TJp+BV~)RA4!ach z;Xxk#jR6X;1UL2kC%fxT0zN6uSk4;1?e$*9mntfi%i|X*Uet4QV&Y)tQp~6yqpzRS zv7|mfe(O&p#crbcH^xaz(93Hn*;ch>$&E&t+-GDQv=Jo9{9_231ocu3oPy*wdO{2C`u!T)y z9@&1Z{~8la<|Ex?z`rO#weH8oELob-hx>|&#hqgSC+{;@crj<c|3D7~vt}A~bHq2|PYMN|$7KN5684Z3 z7R<;a@uu~2a8CW1xb=0!CJB7PiZQOE64`bm3J5#()#&>xk$i=IlY2SW5YL7_`|7q~ znZ$0{nn;zSe82kY6HIy0RAA~@O#LS4-}!4w_syRleR1caA(k^Fl&MMv34_KX`yVsA z6nF;atFT`TcKLlB;Uz<} z2$1j@em1&j_G}AJxY%rSje9kR4*qN{r>SMCYO^k=CC z)?xDofNnnL_huD{39D#GZXBw?>UrcJSQmKh_=wbkUDah~%WJ`Sud{#qG9ascNEqO) z$b?$XJ^8>?FScm~lxgIQV{)XSq?mwTOz&!vZ*F{FsK9YO4=wtNr{?!khfA-_%8&h+ubB{yZf{)e~ zePo%$qJvke-q-9nrK|!=s~wI0)?DrI9pMJ?zPOnv6s~IrmfwU0`~A8o@D)bl^@~Tp zHayESmGgdbJN>?r-UNEC^NPfL<@AnSy&4b~~$O_W5 z=?9ay@E|=f=^3^jxk^x|dXFl3YT#G;+T-PQNJNN z)K`lR$YXN^h%IoQ)WF*J7Kz;oOcru*RiHV(b_X~YYgPt@C(Pg;D=b4SWOCU0B*Ofk z@6)r2d&xMo%l2QKyoI1jeY zB|ky(2ms_Xp>^QsoEes)r>s-oWo$qq;^=i$9k~KI$2Gtm5}*JM1!UKzHtxAxZPo?Y zN?1YVx;Z>-v2wR12;8|p{IKS>Ye?J^`-v)T1q64;0CkOW=#jLwO~dUt6Z+4cT8l0S zUAgid!S1(HE=_M^Djz?Rz|EL-TebhQ#FEW`s_i7Y%A#ua=2DxazNkZ1YfHOROW2-R zA%8rQg8~q{Cq2Vge7pX)I! zAEBuubae^O%lvks%dLd`+=H-z+5Z5(QyK)2#m1YPNq8@_jdpSN+e4lieu5o4I<{$x zE{p@>kytd;$LVmj|K^$5=hCl2M(<}XK@+~cVA(cfw}wSm9N!)fx8WPER3fe6+sZ5u z8&e24WK93@Zvg#aN;ML@5&}BzQ>Zfs>3+QV+L~*8PcK0aXL4vQrT>e5C2$O&`cFRC zAIjg&v4gw<3w*lo;zy>c=uk8a)!-Y-5}pzcK5QX8;xUo#_C8%|n^`>M!yoP++F}>O z)fBS=ywqG?oIEOisk=}e>8cz`$WNCTS59vL|IZma6E1v?1{+43#=urNly=sS@1J$o zADRc0i9t*HEKr&qliS%Qf}$f+{{mEFKh`SQjsYy+fk(>ytjZM)6BOH(gi>hn z7+LS7=r%9eja8*sS)mE_&zpB}6293&-+8v=rC2@xu2W{gk16+4(Wm=%je`db>G&RA zI<8m+xL*^IIGpIMzv_$s2aIn{x?O-QjkxB~$tCt@)fCwi#;d}WCCn?xr3Le{H-FVm zOYSizCElitd)Ai{ep5DY$v@uET zu0d9Ne3?gM>&p&9jG2Eb2?aJ~IlhiQ+5mAJ#6)=#(N2yK3_?Z&cX-_%qoMZ%f-&mL zjp5y8Pq^wZtsy+EhPru-a9dJ;L+otZ+^bQAHtrc60YAv6n@z6pG@=`P{C=N*j&}BN ztp{fnekaGne+tQC4a@feZizrqg`z81GMd9u;FxMN(@b>5mDvQq0cs0(z%7ZRDz>($ zV8xrQUI%ZpwRfhYTK<^lk#8jchh=7lxA+)4jm`?9VJpHcIW{J>HT&d(l}Irg@v(sL zp*|k|>fY&8kEhsqeVom z%2YARNx<1^LnLMC(<23b5HKTAbAc9%0P<&Lq)M0LbC2)j0lBi*OUB?Bd0$#uvi!$# zOEZ(VEu8b7fESPSge~oj$E0|N$;r$}|2SLZvv7Nr^J@Rz7eRU;t27M~swS4{;a|Hq z{NmKXJG#3)S+P~t;UABMV$>7_>zz&vuB)JdZQ63IA+EprR}YZwY2cI?;MSH-fc<0r z(z|YRdj6mvShY6+5A#;%^Dk~+?h{{Z$(VkKxrYU;*WJ>I=@~mJLy_lRHXS}rNoNGj z_=)qh7^IsM#pP{BoawInwAY&bC*iB}pO)+N7@W3d!yOKv#r4Kg2=GXe{n_36Fl-WB zazbD;q?-_W%u%bhmRzB^k`^NMnI%$bNdm2T$Oa4RS|1I4{CwCUaaBywcoEmuINih! z?!aiBBj(R>qdiMLBuXpHQsi9d0rJ3W$|^VP-uC+hNvHT|MaA>IZMnu{~A{Q zYpcET&*rKZ=1O$ENMS7x&aW2Smz$F^h|tlK@T7{wz=_e0NO~+6A`X>T0JbHC8kqz0 zC=Wdl&Bn`jANPZpTZqGV#F8f1_$*%0zKsjLA2aIv&s=4ujLNK!aUu@IqDwOus-NPN zK2jP?ZY#$#%)$OMQi;4$=$eKZY~`%530vy{a29fc17a}+{Va^mKr>4oSsnqFHDC2i zGI?I!ZTEm>-KcHjCWP@NxMqG{uyF`|_(83z?5=4!m&CFw=3mnqGB3vO8vgO{?h%&b zr>?z7HFRe6iZLC-o$XHFl=w|l1irs4-5r`-1gE-&PnpN(JdH?wmCzrAA0l+8qrxKO zFHkR<78dDW(ooss0+^CB>r_P@hT?d03QsH<19xh2_fMBz)dzpk?S_(Bdz>vb>|ZQ3 zbo;>r&eU?^_WQaDY`8fKeO})mS!GCJe3sc$XWjJcEUI%FuB|ENKUK}r*{Z0AdeL|4 z|GLk&tk)31ytcihw+ej7H+3cXVE?vD2JXv%|M3LSi35uH<4j$jM+BESv?GGCuFHxd z@g}VF-}xn5I2w?dt979+9+Cq}C?O!V?m9~)?N{)7{ckhp%xurMPTXTxM0B8xH$SE+ zlO08AsZyKwoZ)i2bb1vBv^NK=@=8V0lU?>qR7Srh-HJyhBgzP2vtV&6sDumEZBedk zF8&Opb{P?yk81481_}=ezz_d2zh#7wHftAo0J!T6mzu#x%Z!c^dRpv(nZ zy_*r}ST020da6C7Jp&@m0`u>M*!b6a>dhbYNfat>SM+v`wW)!q86Y63rS3udWJl8f zlr7^K>-8B9(}dv9IvBe7ukADRON&jaxRG*+C~{F445$)zcR%i|oH4!9f*1KEH3v6m zl*oFRn#6^2iA8-R*;DKdXuE1V8||9GIxKDE4{zXPu{jR#OgW$xU2LNGXaw;(DJ;Og z%AC!%t>=w{Wg%;=?aS<+V6NqRVl)>^D!O&2$uIAiR6UGuZGD8fMt@?#M+7s*+Q7?w z>}BR+g@!u%k>9l}+KxvTb5j1EUCdbBGEq^uD+oG%i1>dJT0Jz zt=6?e8izE#XRqNF4T4a%uk71EFWAtf5Zs6c9+ z?JL%q;y;74P8N#lXWhZjiv3%_`g#(4!0ulLzc=;##?!Cjq2~Zgew$5U_0g^aJQ`ha z^mDrF0@mO!H17?XkZ34up1P=AUM;V`aJ8fV`k(tbzk~L-PGWvJxQOu{92%KzazhVy z#>JTTDBA0s`a1}rv~H(5nor46auOi^0d!sjL3bYO;;d1r2OEReW054O1`91$3AjbI zGzU3{{ORMtitP7`M3sLb`8x8!Fsrox@($(31c7U8?uu@7v@UZe2j zZ_LaMw!eX(@YHmLRICEn6|7+uZOl-iD|6r)bBy(Abp+9Orp2afS%gIEv6XBOJXEvt zgdB15a#l;+vNzXLJZdBJ6!)bfkb6g!yls1fxMlAQbC>5!C`@x5Qa-0v4odpC$!oF3 z3iKO4$g2ffIo*8v`_)VE6-mS>NXUKv+2tYOeBEKm6ggg%eVJnR(cy5@qT5dDVtr0P zrjyGjLwFzD*z)`_$>$6n7atXlZ7{*0wE}C8J1`OT9->P(5sSrV3pcql`q7z$f_CbT zEP80!&=B|iqid@Q`5#+u|98jmw`xy-O?|P{)R?6S4AbwMSjc-Pa}ct5+yeElbhn^Y zjQQ&L8!23zH&-e_ul`Y2RaJEj@!`+9kWbl6DA1-UahV{v;|EZtJ}{oJ8^(K|)z~4x zeqp9RUZopj6ZHl^xZ@{K0i2(Mst^UNO+NSY=`3$~`Xue*Gyj%Uf>-U?j=@|hh4{bm z4DMF>2_}?Y&OB8o^WM|g^8dz$0GYsa@wXJ@2Xxh7F_XsG>5y+R)z0=}TCv^)J5;da z$y_<=tMjUCj3V2(eQA66cC=JKU7uTNBC_o z869H(e1B$UGL2XX2ng_d7+(UZI$2yxw&H8RPHTU21JXfzg363z8=sXcv$`)jo7J*i7tD%BBn1p#A_6QC1u`zQ+-Cj$;{utj$2Q~_>hyl8VlV7cNVwp5TcPz(IbaEH zKa-9_@JKEwq~ulpw1<3RAz>SC;yo^rQ{Cr2`EiubQXi{`{UcA+2VA2mPCfq)@`_{C z$4!)H11D?n-vGa}Zi&n9OCxJY@s{^C_aOMrjQwMuXo?A+&U@B|^yVy-b}ha~%+qdr z10b30aPM%$M%Q8Smq(&;1<8yU!&6NBbmOBMFnDQdD{OFwtp0db?6z+{yu0;#oK<&w zA7v03YS_o7?ADY4x(2VNNyb@OJ|NZg$GsS<_S7N2pvQIX$X5em?|!xQfolqrYSpOQ z8O259miF-!Win}GG<(@VT~4bXg+o6Hzn8ngG3s*4KvC!`d3YI9;TTCbj@O?+X*6v_~|BmHoX0GKHIdPOu1b<*0 zJxCY`+7jBB2+^PH4fP&n4$ur?aEwZ{dT;&hO8i5f8L$3S{wNyY)bTU;)>JFnjY&F| zwfS85KTz=fcPCBV$OF0QWyEI4lL2ebc#%0u7UX8#3SR7{cS>Je?*jjH06j*20Nj_- zCgng&bwFB^BaEB?KmZJc6%d&vFR&k{RD#pR#SEXE!CQh0|f{MS?QpH0`i zD!}|U-mo5p4Lh=rsjhvg8b`v7U-hY93>)E)LY$Z{bclgGndDYlL;=X8q@?^*xsUVJ zNUb$fV7FAl5&4S+ev(H1>S^r>6#r}diy{Yv$3_raLd;vXQ*r~4Z4?S`z%!Y^ME znP0cIuXPd>!ANzFMYhH=tB=Mt>y8_qy?shnP)!Z5E2xC0-fNJNo} ziF%hb4fvn`Wj6_8@5v5W3(gyz{7Ka5j}8XbLi13-tLvQzEECsbItH)R-7TzLVk`l8 ztkqfFPq&@AKWnX@Ao%wNE@8U?PTMY*8NWUp9{|+urtyj4hk$r1w^~zZi`&Y~x26V~ z`a@}IuTRV4@r7Z(#~DYu>I8**T~khaI98R`;S?6WK0mDvRduJI={6Ex;lo!@?Hs~^ zbBDr(YdPO2$0c-Y?emfBM6dSqCm(GbO99PioFT?`H6HrRdHaFl@h){jAFd2e+j%WX zQG$;{`h|f8fGgF&g zCT#Auc55{17-6m^PFkM7_lpPiSC4%Qk;W$OzT8Drgir&-}HBVLtgUS-z-^3+Qc)Z_7k@*ODL0_p?G;CU`8VIe|&+E6p@lltatclg{+1qNf0c z_e8kYJSLcfrrK0C#03~ zj~9+t!VxBKXlS^6{^<)aG9w?J?nfxP`D(T8IMpCNZ*VM%j0WTGL}}m#-t{rS&Rz5#h(-#}1r$KcM|AYw&;qsyTY_c_A*dqEIsg*K{GX_KnPQk<7pecvuX5s14 z7HAIpUZkbP!7N?H;#{I@4lOM5qftC{7}ZQX(XzEyhL5oVcE^N ztZzYLm{qeZqjnT{%V@D-cYXUME+R*_6@*ZcB~8+XVb^o_Q0UX3!o!*KJO=rIZzs`5 z{0{bK)>d$^zvu{M_~$Ureppd>ySqA!31|+{2@v@0 zTXL8R$NbAIEqyU$0Ds-ua6Hm1onPSL;lq>f`*Bk9S~$xo6XBfg)xccvO(L_pR}()C zI}ORxE+_EOA6zY?g*l$~Dr`>i_qul8uNu14hMBO$f^wgP`$9(gIGEMTryG|^cS+85 z9CiT3T848Z|JmgZgXFlhMtU2`#o_7#ARpIdFxus`sgHtE?T*lTK;r9RuTccvD{;Lz zpR;5U8l#(G?!;E;p&<&H&CU|A%c-A{IIz(tiKIG^7~Q7V&k;gk)%Unvl)vvf7g2`4 zT){1H9qzW>UxO|OEGVDjaV6aP!IKfVTcW`p4!`n3cA7r)E&K|@|AJ7r7xZ7{$hng< zoptklFhc6>zs^Pp3qO3kt>F(Qgx zy$BCIqxx<<^hmY(J{Is}SRF^pbD03-J+kzBn*g_1opuk>0QbzSa^4^XZ7%1hrmvKGp&_xD%Inew@=Cuhy}HGt#6Ek;PRw{tQ}I&=C=%9f~Q^2qNs zcR|9vu0@HQ4RLoc-t8Wp+0)TG2v1&d&PaXs8gSVxR$U6v7(CxJFvp^BF8A;ji21kaGU_=@KR`x0D-XyZAq)oKQv>&cso-J={Ft`VYiPbDP- zs}bHlco%;SvBnrnx9W%s_Dy}ADDX>-k^X1>ppfi1<&3)h+fLY*~O4?N^(d%^(LsjO=I${y>;_bb3+yy^mZqsv>#{D@b?yJTR@k?y?>A#+XF}T;k{Y z4{Q6VI@sDHbWfHlVM`@1GJ4^@&*rC>!*rlEojXS8RleITFXQ+3ZZ;mo^LNGDtA<=b zsJQ=d&bERIx3lN84E+h`-MZzz{TB3qfZl!|K;G2<6LRU~%#x@oV9S5uJmLQ#UjcFO z&wF6=`+vQVW*H~CbZ|ps_7}!XL`K=P={i)Q$!0vu(L`!R3oW*@G_r$tV0y}P@0+Sf zIvLG`XM1s}8VFXL^sii2j;yOWv(DUVWWqDPh^22qk3^cC70q1yQ<011omm@31D&yl^9d(k zF0Ahe3yvu>5pR{oMZNj_(f5Upr6`kssT~99#UXz30bxdI;7^egE(=$pm4> zSjQ@xgkyfqiAG)*?iJvUxKB=V7P7Y>_g{X)bd!|fR(@mINgBNuqekcey0Sy1nbuqv5hF64;Jg(o3;gogm(>TJthI?TPBmUXWO?IRMfL zlU9oSRAk2bYr@~&byTUbReq(#`D_oDJlQRRs(e6lJ~(4lO1z55i9#1g$5K5_lH|*N zn62Yg8J5M!qe#%8+P_=;F}Xv_CWr_C&96^m(Z4gSvZ2ydb^IiQVK_IsONiPQ$Y@0D zw0)?&5>>36iU1p3&zYoRkTEMkBa}zt?N$#+-w}f(1Zmd#aPW5RLMHHp=g5q&SeKm0 z4AO4i5h6cl0?yZuLVIXTcNEa_WKhE1!gaz-IS8YAy`cHrY8B&t$Un%m;vncpFF?hkg9b7 z+7RWaG5!W&LWtjEOLD&Vr zz4ri)dm1~uj4PU#Ao0cHKd>;1xpkRu@$Q}~Cx;Q1@&XtJx=HdGmmj<&rt3)2tkQ-M ziSqmCE|kNHGyx#2h&Cs#=UT7$^3xyzG#GjsJS`3J9KwiLzr19SmF5mfx}e?$_hA~_ zTW_ztQ7L_``D!Z&Q=HK?C>@i%ffoNu6)#>G( z^(w|rLoj&*SH!v;@lh(iSsj1v0@45TpO0sd(1yphFg$Ts`~!ZQRiref5^wZ;>{$w! zAQ-c!*7L~?twmeRM$F$IW}kyt#8H`j@Rq$)X0aw`Am{b!PDI{^SI;;DS&!x7m81e> zcR_cTEVz?D{F^cPnrVGszH<4U24FXHG)dTfpmA5DCI+;dFL8A`oAhBmLt_giu{X#^ zw41ZjXoK?rh4I@PO)b6^0g_v+p%oQGmtjRV{>m;BLoJ-xu%(^gHD#ZAVBaKJj-I_mapUkIPo7FO8U(oc z!ysx_LI@|K1~UCUXV z4XB)%G^7S4?ZtmyImz|g4X8z;Cf%)hS)vU)F zMA&2u(*Y%Nq9MO&m7Zd4;&~W%xyjVm6=RXPd`p=;B{0ZbJ_a|pPA_fT&Lg4`W}_JW zuFl`M0{y%s5^yqRTn?aBM+ixi2@y+oJ&coqN}gN(J$|FgOMxvM8YS**Plh6%kOFx0B+*Vc;FFGo58*D#^-`mwY-|ClhgHLJS|OLx2=fCMO%v})Pw1D zO32gSlCXN--GfJ#%)o2#Z3BEsrt+(MLwij4n^d_|C4h*(ir71wW0~(dvfuiAu{aQ~ zFg7{s@1B@2u^3|Y<$G|ls=pJtzUQ<6xE2u6nDLJlF!1BqM@^SgpRueGPW55E4=>l(^&p@SEZhp>2#7& zMu~G{p~t6tHnzzu2s2`#kzx$B?mANCJdJHCK0e-{_~6K~^yzv0JHbVilGO7qh>h8R zm-lS|67W8O1X;VFQu*8_cf8rS+9dXdu2bHyPOfl8Nk&%g!^tkmUGj{wV`M1O#_WJ0 z`mt?3en2|LfQwlUMvjO(`11m(DDeRl1hxnRtVBxysbiU1W6r%P;>YzCCXXG4m0sb5S46B*nm;ZSF9 z-N%8)oLFteRHx@ITa_zpe@srZu3~*1DfOBM?zPp^GHnQ&WYYktG7P(KP2i!{7Y@w>R)cn zA&oT^JpZf=>$h5d>VpWi8M+~1`(h;_Em{|!RdukT{)QpnaEwD+_`I^$nTg@yF+a_8 z-T!e^uYdcm?HOJ#R7++nDemYm4a_%WCPsN?JT06#f6eNPu9?4kAg$>ph%414*A;KF zm$~55Muy&GriRU6Vlxj*c#AVJc@cy-M@&_85nA(fYR^=+QbYZ2meJ`-s5rT_^MpOJ zM^Z*1@?{G<6MGDFHd*WagpprbH*k4e51krFmm_G7Dg?)b_2Cd@Lsk(}} zDbfpJz3J;d(WU1DhRS?SmDo(JR<-AnbE(*ulr}ePRH=`=W?LJ z`<2*^?ey(uzkPEbgZr)4GjV9?ac>(?UM*@`v}@1&Iui_fRGuA3(EbT+=I6e8fq;S$AC4|(;d9a67Ou1+x(J< zS9OXZ^q2Bxe0~?JCO-c{^UoatC=AnB_HbFO=tiI1@QNQG)kg-R#;1dyf|0~YH^hP^ zKjDd|kC0? zBX0O#aP(hJIWG@HlUAnR`4IWTyG7?rcQ6Yu|r@%YW(Eboy1Yz=aK6)NdbL%sEhY_iBvv&3H) z6W?|?638mK7X;HfsNPaoq8x2dFlJ_a$Yl6()fXTY{E>k9C{jdIzK^JqpO4J<$cFZP z&T6sZ-!-m!?ZCt}b#NPbFH5}^#bob5HEXaf8f>tkp2mzH!Ej~%y>8+@WLZ9i>&g)h za8t;3DWpG$Ao#!@$-sXl$ZOYkH0*0g^5|@ndCs*qL&~hk~D{}&2V@)unYvcOVix~ zZCpamYRAUQylOGb9Pc|TAe`BnXt@R!*OZXg#_Kw$iIZQp5Wb8}2W6DO`X?-iu4mz>q+ z7FL`mBk!g4D?)z-rtnb8yLBq4{ZwVl;q&b;*p3|NE}QgVW`U9Id?ZPl*!&TmlRVyc zdkQBGJh&6w)V1)SRA#s$Id0Z*q40HV>FN(2y_HObnpSFB(6A=By!CL?eI@(74&86p zPJ4M#&%{I9*8yj4U+x=U8Q4Nm|BB@IdArCd#${%+Q<`5K-s+jS7>`(smZ7WmJJmZ- zi5AhM9~2n`lg>PLPt`I%w9~NA*0*WexjXuBPW+&RVk~-DvxJAzweMchvhaC4BqaH` ze_GPPR+`ZbkQ)VT7N_NRPXUl&QG8*1VFF=6qJfN#OsZ9%VY~H>WzK(}KE+@Ggx5&y zBJAjMttVDu{aUr1i&#=2C`T9w^{$@Lyz;;QU8+^%e|LSV_VQi46R z3srS5iPmiJjN8%vb*t|4toA4qmzX65?TGM{N-{E@WSS#VNQ62zS9SW#mUW7_i^`({ zAkPMYkGq;glhygdcu! z5MA3ou4)F_;SVrO4e2!rJ+ zZW=AM)wcDXEGew=Cl7?=y-^nKN7uz}eRuoKkB%cD&s@fIV?(w+zsKq^c<~d@5ux#H z|DxXf|4Aq+M8*70T?X7L$i>*4F|--8{zPq9_KV)gh57z}QjzZ0IWk@-ON*kG z@oh`6B@~)7pJj3|lR0!ajy^ar$(jcTqS1li%FYVQVvHJ8J#*%u{|{CG42Bku9~{iX zTMy%cm?U?nH{ona2hcRrw+-Frk)3p+)_tSV?6yF2zV1_i9XWu>(}t#3U0A z27gSPTr9Q-4#h-8M+GE~nHMdqt4VQ#g^LD?wu_6v79lYoxC%zj1ADhtudzkN+rIAY zTq1U#Ec~@xs~Q`fIh0UOSP6Va<_F14zFLswgl)`>|-s% ze*{|u56!i2Zxq!yfiW9J0nJCLL)h)n?)r}HUux^i-pqhrP}@i-aj#_^haW~$(_q~MP}ytJ7zhDlrX z!9&W&(PzxwKMu*eTFC9?N{oK$ezc1G>?~`gI#e6F&F4&IH4(zxevd(vCxdyFS@Kb2 zR*RdN>W74r!01~VIrG8&1{Y`3iAdm#if3c{@Lq$1&p^$3-TJ(!!+T{T?kA*#{w;d{ zkE%rMVg~tR!v7slYRw|02KJ`-(hr0LtCsnZTnNO;46$F z9_QMr<%LE1_Lktd2_QSmwe3MR#KG=GFd#rQ5a5t)qHlh@le=k(vC{Riu{ZG~VNLPb z?!Q=_8u=Jle0{c>4AgdbVJG2YZhwhXs@eJd#?aLY$Gd?~QrK&O#`TbocJMk=;OFCV ztt^i=jsw6#qL@4AlKu8831--rK)`ezFITK+a8KZYB#3QPsUd0zumqHx9Ogou*-ujc7+(%}p&EO`3;f{d*~* zPJalrR}+P)Az>fIu~Lr0Hj;2AW?GZK=j1NHv`E*9*UFA|j_zS5L+ zX>>fzCMK*Yj5QUs_PEA}=EL?96}qijl%%nBf;IvHE{w~Fvv&e1Rk7x{-YnBzE}l}T zHauwrutPN)51@d|RZoZO$5_{Jb@GknPt7xC%i(?;8Z?HjUTiHACO)NvrIxWi8tVY*av zWBvf$$9S^#nmFdT+nlL5o+wDP{@Bp+7G_7MV#vk3x1UHxrq~yBOm;cfh6_r@`e&n` zl>CnKqaGk5!OSmHhLRj@fX-@-Pf`Xra-7|t@=#E$FZrdmp+lq*=ZYpoE|AcMUJ0y!VNaG?;sOLDMV ze{8E0q?C;2bZ+7u6xQ&O8x9DTlc-u=SwUtaP>X3x~AP*hLzSz~kfBx0+&~ z-YY49t6z8;A56Z{l>@@m)CeMbpxc#0Y5NQo<(iMnb3&xzTRLHaz5oIhzpbXJ@(&tM zLCvNQ$ueb=Bi5U1h$73Z+myUh7C@mAFA58%zHhRtvN%Ms@{ixLDDqG^{+RlNYe6Mf zc$F=bUJwLY{9$bFXFmO;M{&RT6p>R=Os4;!PxS~~Y2CJ~-umz%=432`3}R1U6oPp1 zCduQiEv<9HueVT@iZCnB4|$|UR#m@$o%!dU)K;3XIrs8E%Z$1T-01(2^N=z+IIl8w z%_E-DI@5r`xC?F&jR4j4ypGX0C2nf8<9eaeG!4p?W;Jk*DjN9#56DZ$E6QGSh=N3_ z9u^jmf?t7pjni-oRu)Ux?>FWfJh%na3GwC9y?nyJRY0U=CW24=@LiyIq*Aq+|BDLe zH;(t|u(rU5MmEHXKM%b`t@)Fv#iI`HQNQ$iZU>Tp*8FEno$jFW5m#5yGX{&7w!S@?Vu^oH zVi{;0fqij&=X(gFS6K~0-xMgW7U?rot;L>d;%d)bFxx$P{WELN`4fxhtD*w_`ry8b z&+ZRX65EAJ(tPg)alfync-vi|y6JzB#WL|T24KK8$$Lh}DU;hhAk8j!@rCmM0K3oO z524WUXPveDR#UWsVlrIr2Q# z{5};Gf*IQhmIsYxn?&;^X0`!ej$@#zn*KJawCsSWs*q6xp9*X>^);y0e95cc`<0Q{ zP(W3p^rgdqnWl`B3$2?jdOR+ZJV$JM7{u0H(LXr;_-* z5QQP~U)oLG&v|F--Ht4;dN!QvwHjg8Xkh!+ncOH+kkmlM8h^--Ix-_i#Qk_wX`K9C z`L`XJrKSSfVJT^#S<|E}3BzVqHbKJzExlB6_dK44?BQhoHW34;MB+LkJRxg{c9=08 z<5vOb`S23Q;CP|z)ewu51(_MMU#l(a>r-EPGbuRCjsPU>0(2aghLwDJeyRvt^`=PL z`1x*>>mk6+TJ*ZA#V(N!G~_2Q*X#Qw^-*qYNP^>Fcl|}$ZXI}OR4e$0aiq2gT_H-J zdXF4$lkI3^7X2!MTXOaP64@=fyEgqHeUXZiPTlH zAlX%2Dnspl4Fu(HB(LAwdql|G)DzUDHERi_x_gjNy!Z{_a9Dd;@>9Y0mxwv@i_A4% z&<3LNvk#=8ZBBJV*+q#f?9Zn1B5$z5{$Z*AfzSl5}Tm!}e^TMUnc? zj$GYh2K4}>U3%W3kX#3enu_SIZTP)_rF}#f+n2)JjkI1cEXS|Td3$CI z2Xz9UrWL%RBt?=Z7v}a4v_WEMx`x;WZBGU4$oPo6M&Mpst!8l zQ5cq>{mBU))0^97w`}WggH5VgVZermmjeG0u$Hit<~%9gOV69$m{j!Pn#epQ$86IY zx*WUPa059{NGo|AKPWErYtxR z)JS#iv!W3HK>)&_?d*@A;n_0+|rZ&@<0Lf?-uW)DSby4jeh@%HE`*6re$K)7?dcYX^&TfFetKY7Zpdf9aZfSbH;9t? ztqp-#xn)w}vyZLkeqcJ%iA=bUP&tFFntS7%%&7RHVVM}nFDEV7kY+CGprtIkg+;EF z@+K{?wTAohlR=ZGSPI4u-^AaYGkxZI3Wtr_6Z$HAKu;40@?;(R!vWKa&O!7)Fr#>% z%LC%G$@>M6g+0JGgz3%7aFpt5>4>EO8&_}cH_rEL2q+<>lf{^#4;p?W2-^%A;D`*1 zli!eo#>YwszK>eps&HVzj!h$3W|DChJ4nPjVivft1jt2M3B%;s!Mul(~6V)OVURxVitpW^wYMWr>wW;e#60 z+238Tu;`POM*ooRG;q)jIVUQv{>S58JgBy`ctVj@ye$~P>Jd&bDP_5ompX1>&j$xK zt0K_}ZIO9poV`7;hyaP*FaPlzAOB?cbE%R%N&>IV4MEe&_%sPNsK~DE9Ny)ZiRN#@XzZMP62JXxwv|u>w3k5U2~@BR zQ>D9rzpoidMgPK=fG?*H&TjptBB*2|VS2jHhcl=E(4NpvN99~>{5{;zxuCjK;Xrf` z-G^^?`z`A?#PMyZ{EFIHJf1jMcP-~2(e1wXhAJDc`hEn1iUcbWn=*jf=r2f?@tbv5 zTA>b21pqF2W4@)-6iNlv6t^~RQMzU^2Ti9|dz@;y-bgT-fpP*(R-C=oz2nU-TmEiT z0%BeaGx_3|yoIt#iee^PGYt{l%;!6Z%VK?_!!yI79)m6Q4nXn?OblFI2P<&TP3y4^ zv^pdLSWN#4ipROLk(o4H0W^Ikb_=wzaP zmo0y!Mz=U<8b@`dRl3&j9&3M=0qoDzyv&`y-V1vaYmznqm?el}=(bG@IYlcbf_P6C&{9YCj#NU@!mbEvfqi5X+ zCS%J1j{2Lol?jxlub0o){x1BvmSx0zXTWsaY%z9tj^dPfLF-AqvXJlYxbaMRscW}m ze0K)NVWah@jUo}L{yXV#!$+n(Y(SFYnedTHlv*l}0+~bTm&E==hhQM{gVY3RF1L4D zr}tMJW-Y&(qVy&xy`ZjkqV4q0$~al)`k`mZ3L?rLozI0Lukwc>$=@Ykvi4E} zHC;NbxZYBs${zy;?U|4Uh8HMIoLSu4?3}Xr*2Fu{kr4U4^ki9yW<$pJ>KRFrrOG*& zk4c*77iMtz0bQ+NlQ%E4#9uEy=;wgJ=m>%w*x!E)tDnb1cc<8)4Qkf$|~S?Z2JY$#5N4s^CvgsTz&2a{ACK+2KC`r!FTxZeUt zUaM)`?98MG#2o;%$hh0wc$&Eu!hvUIL_K42fKnT$k{$7Q zymH`q#7@6)V5oZv=<|#EU%T)o)P#($H?^xYs``!(gI44HLiCl}D~}0HSDu-4tcfm% zIAQa( zI2<$RRW6I{a8~Ceu@8ti-dIrz0)F_w9C;Spu9i~eGxZ{+Iz<0DMbH{U*mqotPapL``*bK_^o6eT!C-psuXpI} zS0z~#sA{Thu|Y$UQ^$!IIP?!()9l`2U+k+;P<<#Uz?jELpt6K`x)h!mQM+YWQ!Cb! z_O;2Z@l`CzbVNr3U=eS63{lJqjrCdoVf9ijx+R1#3`z<8l;Qd7{hRJ4 zIZ@@He2JiL?IXJ;bK##Orox{4Zvp2$F7ub!V}c1H=vcU(3#xjlgYooIY28IoZc#-3 zEsjyC$Aa6a9n_|5_^6NSj0#tDM;iAIe*A|rTLIKQT3PUaTZq3_)${{~RUkt?ch~WB zZ-_^PjI+*pp4g9pEH$iGdu+cI7;HyR)E=K%DrHzUTMf-hLbb25w(Ua`P-9*RT-y6@ zV{Fv`>RMdIUV_n(rbb;Y09vj?pek`quEEK;W&6^$mi$H!=025>_b?otWjv&PdL?Fb zcwx*_?WYc$>sn8`Ecry_`iGF~Gd=8#=4X$Zh3)#z1V1dXo$c5nuM84zy7c(Lp%byP z1A8el-61yJDwQ*K*<$ki*xp%U-B=@$T%=6pxPF|1X=;hz!hV9E;A6q5kgY|MX#gv{ z*i$jTpHtvu#Hjyr-=XWK7;7@}rs7YOlRo}?UzEg0FkemWiPVcK1G_HSECD}7x!hL< z5~IyuXYW6j$HWGof{RVp*)=RD`UvDA2Z@J+ylbY{X-f#MXN+1tEK{ z*H2=!jLJjeJNHy}80wEUjHFh)b}JDurE(q~5}8B1Ifk*Hs5L697q37@Ic%t2jvn_8 z&3krxU$ifW()8rHC9fLx7{lUazYJ1;B6EMO{Bw}W;^BM)BjO!hjoJ`jNyTm1I7_9^ zGV#|j_HeM>5qSeTUL3C6QYx|lSy4j+Tb{>*0 zPoIF{>5dmz7$?ots3C!pLf7qN<(%Zio*EOCmcP5*F7vnRN1G?Hn-d!I6licVZ|5Km z{ToNWC=L!8SQ|&o$bDAbEA8yTs2Al^$tszZ^7* z;Hco2_!xqxg@DR_*6r5nFVti>3Q;nQ@ifmn!|3-Gc(YFw?H6AnC(0Z~Q3{u@R4QSU z?rX8X9(=I*+=N|DFJ$#feQ#=^LnW$QG)%n<$jely7e9&_Pcd^eXFiSH>e)~SB<0uN z?WCHh(!sh7g_x>t9zoY7*aBR#NY*&D<7*8dmyY{kx75zs2}2{j(~Kt@>JdV;qCb`w zXkGUd>~bH&PcZ6l|Il;r&-9}}gDgq6h%PCRt=4vvP$jozqY>J@ll@cd{uVr$v{%YL zAF(F~Bx9 zb0u#?o`$1$w#HeMQUI6XKgk260V+RBE4U3OFCLBJ=%_Y3FEFII^xtQb!O|;)HuB-4 z%nGrQ%z~YDY@s+BA$T`*g^u=trQ_ySddiJGjIf}eE9`@v^74bF6j%lm0GBTL zm1dl;t@|Gbgtg{H?THA$oFS(4N9>#30Q(@jy`HCU2Rm@TQ@1 zHTD5eW%nK}=lMt9Bg*WiOOm-zxa5ozkP<5}jH!X7zm6H!(`2X!h3u~tUNId1!d?pH zHMc%h_ySin>sk0@&|MgP+czUtm4m2(huI-5_fyzjO^?VrATAQv*8%AAqCoU?{_JDm zboDf*-BS!i{b}uqSc3V>m3g3{G7!@jFjM-&TewL{P{f_9A(smmpgDPgmT>G#HXVH4 z@Nkb-y9&?!+U{iM#x)U{unda^Ys8#BMqP0!)U3D%5#XLF_}U*j?r=GZ0&cC!R!WA- zOt=iHFXf(|DsZ%Bw(*NwL=Y`oYFgFTPt^_%c~=dd^3SX|KG*e=Yd|NIJOAm`z4T7f z-l&`T>+T18MN({3N|||_ubfEow=TYg~4X zY7)*6+$0oOEL@cEBYi24&xP5sxO8%>>Vr=<#b^7}`0RXz$|9rc%eqDZ$i|9cDZeOJ zrbBiQH@vv{>{WtCPS2GXc3g#kcvsPuhfk~JYd_qCKAOg4yXTdQAxd+RcAD?WByJ29;7W{a(pPLCqg13Q5oA-`f<>k6>;>*lWIU(*&A&vLDDbcsZe zQ;%U(5&bKr8F7SV*1n|aF0IkYjCiP4|v$$Mj5wE%cX6G-M z3J`6D&iA&!1m}{!Go@?LN~db+4G9Pg6|w-PhqvPm_3Z$IgPpo?Y^!EZX#sHs0 zzP3yA`*}IG5VmERG`mjv?4`Aiq z0yl5E(&5_7q}Jy-6I=bx_I%Q!mDM`jGWaA#j6_yMps`VR=CHj(D8Nvic$e!Q8wbXH6A z+k0;tXIXtxha|z;RlX8nh``aBOki0lCEVs4S~q>!BQz{;*;3}aGbzcgWHX*G#ergPq*y=&l_`YK^5RgXm+GFMY{G?bZr0-<`siD} zv{os0#i#UuLii^!9dE28=W+p*&N)AM)3Q*$Iv7n%QL6=f3Mc$)MuqsStIDAXYk-0T z=!?j_F#oiLE_!ymQrpC?W(Nc&4PVYpMad}UG`6I+ET!4>1#b9tt}I^W=Y zcg7`AD4c0$iM@w>8(lR%ur+c?+?5yIYrUi*i~~yy6PI{r+wY(A^;L3zX^B?Dd<~@Y zz&VX^4r#a%#8^h z0I}lb?tqRu-L$4CRc2g&K(_ZTajbzS_&r<_|iCW;LQ6KcC#p*vZw2A~=_{#U)G$Ub`47h)UYDS%?bsJo~zwjV=h>BSk1QK`!->Zf{-q zQj*nBAa%izx+L&5*GoitjnxCvz|l(OPgT9YlGsI#r+XA>im%2UI$35h1Oc^CvJz!h z(-k8U*@16e0?=8agS(Gwue6FjGUbf$4G+@-#<*+M(xKgZsew*vj(;i|tp#k@CPFIm z-F847Z5J|w1vIUH$CVJ6y%WHnU-_nfkPH1?9R=r5c)9M|Eywx*_^jhequjmRHLyNK z9CzIoAz&&8sf2)%@6OJq?SL3a1g9QrYLCzZ>htV6pTrZo5(yyXA9V~sq0wo8w9BHoyA05{O{MGjH5Z^JR2yqvvIv642*-lMr zX(g0;tpj*qJzM%nW6q&$#2ENcTu#$QddEcrw_csV=9V%+EGob0#|36tRHu0n`-Q3=g9;oV3Ntx z{=pFc!_^UzsaE;NwudKI65r1lNgUR_Hs}dU-Vl9%st3#h_}d z=Ze+IFq#^fEKi>5wGV1fsrqHA`{iu-p}3|}NwZ=gnS~8SEu7kcrj$MXV6-tXiENh^ zAP#X!t~zaL)9rwB6`u_M_j&I!p+vcyaVsGBbzH>X}bNdYW=Ld)?le>6T17u6F~b zHZ8sgB985rYG2bQUz>q&fY~n^f?ho#-nF*nDKOQ!xF|+#gJ%v_18sy-iX_6O+)ZKQ zzv+{32&WfnAkWg0;fhkN!S(`Q6o}XA#@D?xUv?`pPru?5>>-zYFXS33{~=M|Jj6>Oj4S(UctYj#hVl_&FyXfElC|5XLc>mS7;lD)~|8p4Te>n`saeZNo z`(KD+HZv=)ayfftO_*-(*qY8jX}yMp#LIdsoNAgpl*2;)nrvV6Z3tY^W36Tgn{J}i ztEN2!M_sR~d3WtQmz))Mg@&!-^kE1Et&%i`iLG6?w*iAvG?D_s@hEZkhwBN0Wgk~g z|MDsH4nx(*!J?f1cL)m}K=kCc%H!dx{+TW<2F|A=UCf)yoW5m-KIIU?!F=dIA z0m!x*9a3)g9Rd+aYohO=8<7oZC`~IVfMF!sz+w6Bpe2){D%8i@Fa5nKSm8Lsjz!1p z<>j1InUEkdIWeL+^6F8PJmIJ4kiQD|1q+L=V;Tg{cbQblqNabj|GEh;PNOkbtXdJR z-9zIPY1G=M$~Itl68=)yT)4nozq!PTGt4-X-c!ZlHSJR@&(*jcKZ(w-82A%e)C=#ixTd#0#`+R` z?2I&XLb;ofwS98i0l~JH+wRXt8U}~z9gB8CBKy|r>Fg!V^|x6B zs+Ks%Qtvw^skwa+@YfEE72^7opeZOI1bdWVXLYQQkaCQd0N3^8?eH%OReAsHoZL`$ zigY>1Xn{`6MS4?P} zB)p#&YFSwIWELFADJ&lrm{nc*9tPk*V8nIyQ{4CD+>48ZN~71!s;Dhzr~($!))vPW z2O5B`vh+-4$9$JYE#O>1L*%7`7Ll<_{}FfiK=Dj$=&|SO9}%Y8xWEE`Q`1`=F|n<0 z9)IY=w>Q;WUbc)3oNT7jxI#EAEjY*$rcC$>bU6b^KsL_xc34tJTjDG|&m4E-QhjyY}f;NYK7ut=v&F+>8&& zPri{(z9)}LKXZHgQfZPsNCo{#$4q{oIlVtMk13u z&l?(T0~X9wrKhffT8uEJxm})oTYpEDV=ZxmV$ahPO2|Ah_u8bJV$~M;ZHtOa0(SjQ z>J{9`-!w|?*w>;yWZ6arbo5+Y(t1@m@pln!neLT7^D4hgg9~J2+l?U^N@R~0Q)5zm zk#KY{MxCY`&|VQ{Z4>s!w1SmXoSp+w?*n93a{-i@7CE>dqbkebF5wjBf1ad4Y@VhU zwdDH4*#s9mhPCEKo!3Fs41dY+zdK+khJHnk_Q>0ifNd9GJKT07@I zcQH~}on{VU@A6~7^4?II&b}n6nbJk;T79a2pIv=rmh80ER^bSn@w_zBxBT7afC^7u zYq9-e9`(a0mF6B4gMUVM1GDHved07%Wg~M?Pjc>WlG6&Qq*r*^P4>dca~~J_sf|TM z*)B==K7av!OT;nnJ3^bzDmkY{p544!Du{l6urzW zJQ9~m?rfWidPLKI0LBgCFlqW7gF4{RZMqQS_pY>Hl^2_ljO$1K zCGs8@j8jJ3FjjQo;Kug$*>7N)Avb;W51wq2wl~0}A4WVt*hbIvL4Y(#6lAK(Z8Yhl zZn+uACQ{Y9`z|8FU3ep4|=wB0+Mr7C(jwe=1 z!oDwlI`5XE`A0!}MycvoF#{~HYcH9*St(K0>&5_3A~E?r#m8$xJ?dFwYX!U_JY*;{ z8JVR456gL>HG7tYiUxRhtMOKL;0{RY{ICyQ%sMZx=ng|!(CRv~b(P-^@(=KF&dgrS z8968MPH(&MJ|n4vGJ7CeJ-k-Hee+rm}W@16O0-qb{`{W^~!cir=bXaArWk2U% zfj4;Tv}zw-nTe4oybccA2W%QM%1L~+V-vik4Iii-AB|#se-Ct;=3mE$5tVHENi*=lU~9H9%&v+Ig!93Ft~r4%GQsrSIGPp0PM(s&8$ zPjY}fO>6n@}A|FLk_0rPm{rt3KIMcsvdKEZalqlu*>9cPz)}8}}ip`_y;QR^~20 ze5sY`{EASj0FS(SSR;Q=?@wO0LStnawpSrkb=MAbCB^Igz6>i2^~l2^dWQeYE`8QP zv>wLDyDs@{tVR0x>~Aiz`EQlJ8+S`RwOTk>@2){RIyx#?750?3sww_gn%LG>#QjAM zPaIMbKvl6K6R(D?Lv{7#80(Wo&J8@$uT~aY8z-p_juu;*_C~zGulE#jo{@Cny#f4_ z((&&(9H@U!Ncsnf6lVb4ZIfv|k6Y_3!wD|#_Zq~@n-wqw1JwsZ!y$j=lfL>X2EZ z!B`mb;z%h9&8v&@++04{lH2d!=ie&GdZp8JJs)G6(v`o2B6e~tB;Lx(AL?FXs#eLP zRt^7|-ME*OwNVD+6w8(AMiLRGg<};EKHmH^MP4y{;8mfL7Q~J*7BEgnvh|eAT!)# z89;-K4~xqm&9^q*e+j*kpkE?Le|jHtK46-)+1?}1AeT?mn8mmh~3WyQTb^^zD4lZsfzvRXn#f$_e_%nDKwnjm0DYUV{NN2C;B@vqU_ z!JCH&fR;;>ZZ=m>fFF<#W~KbB#H@v@t)tH6pA~o5jC`n74ced1lA}%{c;Bf41p7|e zrwEwtyAzO9MaFWdv38Q`0yFFzO#~Xw$yvS<+dxpmw+p;g(0vPN@IY1Rb!Io9?I*Pk zSxAXwp$ZW=t^__-c~7t;AV3CU}QZo?Rk#Rr_gdb#WeN5FvZjh)~$dqO&o49U1=`1 zol)$CxDPuB z1>t!h(bZ2bk3*0rs(W*CBT@jIe|M2@K@kMExzGMjp7V|B+wLOvHyZnvgs>T_Nydcp wu8m+z3kO4<$BfGj43pXw`@i7w#q4%0uIxtogYN?G|MiM2L{Yj(O3&}V0rvdJKL7v# diff --git a/doc/user/project/settings/img/import_export_export_button.png b/doc/user/project/settings/img/import_export_export_button.png index 6933e3edfcc0bfb1e1ce9870e56eb47961cfa414..6f3663d64e88e0f66f39896b3636fabf099d44fa 100644 GIT binary patch literal 31790 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 literal 14530 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 diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md index c0e08987998..9fdcab406cc 100644 --- a/doc/user/project/settings/import_export.md +++ b/doc/user/project/settings/import_export.md @@ -160,6 +160,8 @@ To export a project and its data, follow these steps: 1. Select **Settings** in the sidebar. +1. Scroll down and expand the **Advanced** section. + 1. Scroll down to find the **Export project** button: ![Export button](img/import_export_export_button.png) diff --git a/lib/gitlab/background_migration/migrate_requirements_to_work_items.rb b/lib/gitlab/background_migration/migrate_requirements_to_work_items.rb new file mode 100644 index 00000000000..017791f197c --- /dev/null +++ b/lib/gitlab/background_migration/migrate_requirements_to_work_items.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Gitlab + module BackgroundMigration + # No op on CE + class MigrateRequirementsToWorkItems + def perform(start_id, end_id) + end + end + end +end + +Gitlab::BackgroundMigration::MigrateRequirementsToWorkItems.prepend_mod_with('Gitlab::BackgroundMigration::MigrateRequirementsToWorkItems') diff --git a/locale/gitlab.pot b/locale/gitlab.pot index b9709b62906..1720b2be13f 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -1107,9 +1107,6 @@ msgstr "" msgid "(check progress)" msgstr "" -msgid "(commits will be squashed)" -msgstr "" - msgid "(deleted)" msgstr "" @@ -1128,6 +1125,11 @@ msgstr "" msgid "(revoked)" msgstr "" +msgid "(squashes %d commit)" +msgid_plural "(squashes %d commits)" +msgstr[0] "" +msgstr[1] "" + msgid "(this user)" msgstr "" @@ -6553,6 +6555,9 @@ msgstr "" msgid "Changes to the title have not been saved" msgstr "" +msgid "Changing any setting here requires an application restart" +msgstr "" + msgid "Changing group URL can have unintended side effects." msgstr "" @@ -7196,6 +7201,9 @@ msgstr "" msgid "Clients" msgstr "" +msgid "Clientside DSN" +msgstr "" + msgid "Clone" msgstr "" @@ -8689,6 +8697,9 @@ msgstr "" msgid "Configure Secret Detection in `.gitlab-ci.yml`, creating this file if it does not already exist" msgstr "" +msgid "Configure Sentry integration for error tracking" +msgstr "" + msgid "Configure Tracing" msgstr "" @@ -10397,6 +10408,9 @@ msgstr "" msgid "DORA4Metrics|The chart displays the median time between a merge request being merged and deployed to production environment(s) that are based on the %{linkStart}deployment_tier%{linkEnd} value." msgstr "" +msgid "DSN" +msgstr "" + msgid "Dashboard" msgstr "" @@ -11164,6 +11178,12 @@ msgstr "" msgid "Deleted projects cannot be restored!" msgstr "" +msgid "Deletes the source branch" +msgstr "" + +msgid "Deletes the source branch." +msgstr "" + msgid "Deleting" msgstr "" @@ -12214,6 +12234,9 @@ msgstr "" msgid "Does not apply to projects in personal namespaces, which are deleted immediately on request." msgstr "" +msgid "Does not delete the source branch." +msgstr "" + msgid "Domain" msgstr "" @@ -12709,6 +12732,9 @@ msgstr "" msgid "Enable SSL verification" msgstr "" +msgid "Enable Sentry error tracking" +msgstr "" + msgid "Enable Service Ping" msgstr "" @@ -32506,12 +32532,6 @@ msgstr "" msgid "Source branch" msgstr "" -msgid "Source branch will be deleted." -msgstr "" - -msgid "Source branch will not be deleted." -msgstr "" - msgid "Source branch: %{source_branch_open}%{source_branch}%{source_branch_close}" msgstr "" @@ -34507,9 +34527,6 @@ msgstr "" msgid "The snippet is visible to any logged in user except external users." msgstr "" -msgid "The source branch will be deleted" -msgstr "" - msgid "The specified tab is invalid, please select another" msgstr "" @@ -41019,15 +41036,15 @@ msgstr "" msgid "most recent deployment" msgstr "" -msgid "mrWidgetCommitsAdded|%{commitCount} and %{mergeCommitCount} will be added to %{targetBranch}%{squashedCommits}." -msgstr "" - -msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}." -msgstr "" - msgid "mrWidgetCommitsAdded|1 merge commit" msgstr "" +msgid "mrWidgetCommitsAdded|Adds %{commitCount} and %{mergeCommitCount} to %{targetBranch}%{squashedCommits}." +msgstr "" + +msgid "mrWidgetCommitsAdded|Adds %{commitCount} to %{targetBranch}." +msgstr "" + msgid "mrWidgetNothingToMerge|This merge request contains no changes." msgstr "" @@ -41135,6 +41152,9 @@ msgstr "" msgid "mrWidget|Delete source branch" msgstr "" +msgid "mrWidget|Deletes the source branch" +msgstr "" + msgid "mrWidget|Deployment statistics are not available currently" msgstr "" @@ -41144,6 +41164,9 @@ msgstr "" msgid "mrWidget|Dismiss" msgstr "" +msgid "mrWidget|Does not delete the source branch" +msgstr "" + msgid "mrWidget|Email patches" msgstr "" @@ -41200,6 +41223,9 @@ msgstr "" msgid "mrWidget|Merged by" msgstr "" +msgid "mrWidget|Merges changes into" +msgstr "" + msgid "mrWidget|Merging! Changes are being shipped…" msgstr "" @@ -41284,9 +41310,6 @@ msgstr "" msgid "mrWidget|The changes were not merged into" msgstr "" -msgid "mrWidget|The changes will be merged into" -msgstr "" - msgid "mrWidget|The pipeline for this merge request did not complete. Push a new commit to fix the failure, or check the %{linkStart}troubleshooting documentation%{linkEnd} to see other possible actions." msgstr "" @@ -41302,12 +41325,6 @@ msgstr "" msgid "mrWidget|The source branch is being deleted" msgstr "" -msgid "mrWidget|The source branch will be deleted" -msgstr "" - -msgid "mrWidget|The source branch will not be deleted" -msgstr "" - msgid "mrWidget|There are merge conflicts" msgstr "" diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/maintain_log_in_mixed_env_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/maintain_log_in_mixed_env_spec.rb index 2b1c956039f..734529f319a 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/login/maintain_log_in_mixed_env_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/login/maintain_log_in_mixed_env_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Manage', :mixed_env, :smoke, only: { subdomain: :staging } do + RSpec.describe 'Manage', only: { subdomain: :staging }, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/344213', type: :stale } do describe 'basic user' do it 'remains logged in when redirected from canary to non-canary node', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/2251' do Runtime::Browser.visit(:gitlab, Page::Main::Login) diff --git a/lib/gitlab/sidekiq_cluster/cli.rb b/sidekiq_cluster/cli.rb similarity index 92% rename from lib/gitlab/sidekiq_cluster/cli.rb rename to sidekiq_cluster/cli.rb index 3dee257229d..55b4521d37d 100644 --- a/lib/gitlab/sidekiq_cluster/cli.rb +++ b/sidekiq_cluster/cli.rb @@ -4,27 +4,21 @@ require 'optparse' require 'logger' require 'time' +# In environments where code is preloaded and cached such as `spring`, +# we may run into "already initialized" warnings, hence the check. +require_relative '../lib/gitlab' unless Object.const_defined?('Gitlab') +require_relative '../lib/gitlab/utils' +require_relative '../lib/gitlab/sidekiq_config/cli_methods' +require_relative '../lib/gitlab/sidekiq_config/worker_matcher' +require_relative '../lib/gitlab/sidekiq_logging/json_formatter' +require_relative 'sidekiq_cluster' + module Gitlab module SidekiqCluster class CLI - CHECK_TERMINATE_INTERVAL_SECONDS = 1 - - # How long to wait when asking for a clean termination. - # It maps the Sidekiq default timeout: - # https://github.com/mperham/sidekiq/wiki/Signals#term - # - # This value is passed to Sidekiq's `-t` if none - # is given through arguments. - DEFAULT_SOFT_TIMEOUT_SECONDS = 25 - - # After surpassing the soft timeout. - DEFAULT_HARD_TIMEOUT_SECONDS = 5 - CommandError = Class.new(StandardError) def initialize(log_output = $stderr) - require_relative '../../../lib/gitlab/sidekiq_logging/json_formatter' - # As recommended by https://github.com/mperham/sidekiq/wiki/Advanced-Options#concurrency @max_concurrency = 50 @min_concurrency = 0 diff --git a/sidekiq_cluster/dependencies.rb b/sidekiq_cluster/dependencies.rb new file mode 100644 index 00000000000..91e91475f15 --- /dev/null +++ b/sidekiq_cluster/dependencies.rb @@ -0,0 +1,6 @@ +# rubocop:disable Naming/FileName +# frozen_string_literal: true + +require 'shellwords' + +# rubocop:enable Naming/FileName diff --git a/lib/gitlab/sidekiq_cluster.rb b/sidekiq_cluster/sidekiq_cluster.rb similarity index 89% rename from lib/gitlab/sidekiq_cluster.rb rename to sidekiq_cluster/sidekiq_cluster.rb index cc1bd282da8..49478ba740d 100644 --- a/lib/gitlab/sidekiq_cluster.rb +++ b/sidekiq_cluster/sidekiq_cluster.rb @@ -1,9 +1,22 @@ # frozen_string_literal: true -require 'shellwords' +require_relative 'dependencies' module Gitlab module SidekiqCluster + CHECK_TERMINATE_INTERVAL_SECONDS = 1 + + # How long to wait when asking for a clean termination. + # It maps the Sidekiq default timeout: + # https://github.com/mperham/sidekiq/wiki/Signals#term + # + # This value is passed to Sidekiq's `-t` if none + # is given through arguments. + DEFAULT_SOFT_TIMEOUT_SECONDS = 25 + + # After surpassing the soft timeout. + DEFAULT_HARD_TIMEOUT_SECONDS = 5 + # The signals that should terminate both the master and workers. TERMINATE_SIGNALS = %i(INT TERM).freeze @@ -62,7 +75,7 @@ module Gitlab # directory - The directory of the Rails application. # # Returns an Array containing the PIDs of the started processes. - def self.start(queues, env: :development, directory: Dir.pwd, max_concurrency: 50, min_concurrency: 0, timeout: CLI::DEFAULT_SOFT_TIMEOUT_SECONDS, dryrun: false) + def self.start(queues, env: :development, directory: Dir.pwd, max_concurrency: 50, min_concurrency: 0, timeout: DEFAULT_SOFT_TIMEOUT_SECONDS, dryrun: false) queues.map.with_index do |pair, index| start_sidekiq(pair, env: env, directory: directory, diff --git a/spec/lib/gitlab/sidekiq_cluster/cli_spec.rb b/spec/commands/sidekiq_cluster/cli_spec.rb similarity index 96% rename from spec/lib/gitlab/sidekiq_cluster/cli_spec.rb rename to spec/commands/sidekiq_cluster/cli_spec.rb index e818b03cf75..baa4a2b4ec3 100644 --- a/spec/lib/gitlab/sidekiq_cluster/cli_spec.rb +++ b/spec/commands/sidekiq_cluster/cli_spec.rb @@ -3,9 +3,11 @@ require 'fast_spec_helper' require 'rspec-parameterized' -RSpec.describe Gitlab::SidekiqCluster::CLI do +require_relative '../../../sidekiq_cluster/cli' + +RSpec.describe Gitlab::SidekiqCluster::CLI do # rubocop:disable RSpec/FilePath let(:cli) { described_class.new('/dev/null') } - let(:timeout) { described_class::DEFAULT_SOFT_TIMEOUT_SECONDS } + let(:timeout) { Gitlab::SidekiqCluster::DEFAULT_SOFT_TIMEOUT_SECONDS } let(:default_options) do { env: 'test', directory: Dir.pwd, max_concurrency: 50, min_concurrency: 0, dryrun: false, timeout: timeout } end @@ -103,7 +105,7 @@ RSpec.describe Gitlab::SidekiqCluster::CLI do it 'when not given', 'starts Sidekiq workers with default timeout' do expect(Gitlab::SidekiqCluster).to receive(:start) - .with([['foo']], default_options.merge(timeout: described_class::DEFAULT_SOFT_TIMEOUT_SECONDS)) + .with([['foo']], default_options.merge(timeout: Gitlab::SidekiqCluster::DEFAULT_SOFT_TIMEOUT_SECONDS)) cli.run(%w(foo)) end @@ -271,7 +273,7 @@ RSpec.describe Gitlab::SidekiqCluster::CLI do expect(Gitlab::SidekiqCluster).to receive(:signal_processes) .with([], "-KILL") - stub_const("Gitlab::SidekiqCluster::CLI::CHECK_TERMINATE_INTERVAL_SECONDS", 0.1) + stub_const("Gitlab::SidekiqCluster::CHECK_TERMINATE_INTERVAL_SECONDS", 0.1) allow(cli).to receive(:terminate_timeout_seconds) { 1 } cli.wait_for_termination @@ -301,7 +303,7 @@ RSpec.describe Gitlab::SidekiqCluster::CLI do cli.run(%w(foo)) - stub_const("Gitlab::SidekiqCluster::CLI::CHECK_TERMINATE_INTERVAL_SECONDS", 0.1) + stub_const("Gitlab::SidekiqCluster::CHECK_TERMINATE_INTERVAL_SECONDS", 0.1) allow(cli).to receive(:terminate_timeout_seconds) { 1 } cli.wait_for_termination diff --git a/spec/controllers/concerns/renders_commits_spec.rb b/spec/controllers/concerns/renders_commits_spec.rb index 5c918267f50..acdeb98bb16 100644 --- a/spec/controllers/concerns/renders_commits_spec.rb +++ b/spec/controllers/concerns/renders_commits_spec.rb @@ -64,6 +64,12 @@ RSpec.describe RendersCommits do subject.prepare_commits_for_rendering(merge_request.commits.take(1)) end + # Populate Banzai::Filter::References::ReferenceCache + subject.prepare_commits_for_rendering(merge_request.commits) + + # Reset lazy_latest_pipeline cache to simulate a new request + BatchLoader::Executor.clear_current + expect do subject.prepare_commits_for_rendering(merge_request.commits) merge_request.commits.each(&:latest_pipeline) diff --git a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb index af5ba14e310..9057b96bff0 100644 --- a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb +++ b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb @@ -36,7 +36,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do click_button "Merge when pipeline succeeds" expect(page).to have_content "Set by #{user.name} to be merged automatically when the pipeline succeeds" - expect(page).to have_content "The source branch will not be deleted" + expect(page).to have_content "Does not delete the source branch" expect(page).to have_selector ".js-cancel-auto-merge" visit project_merge_request_path(project, merge_request) # Needed to refresh the page expect(page).to have_content /enabled an automatic merge when the pipeline for \h{8} succeeds/i @@ -126,7 +126,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do it 'allows to delete source branch' do click_button "Delete source branch" - expect(page).to have_content "The source branch will be deleted" + expect(page).to have_content "Deletes the source branch" end context 'when pipeline succeeds' do diff --git a/spec/features/merge_request/user_sees_merge_widget_spec.rb b/spec/features/merge_request/user_sees_merge_widget_spec.rb index f74b097ab3e..0117cf01e53 100644 --- a/spec/features/merge_request/user_sees_merge_widget_spec.rb +++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb @@ -426,7 +426,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do it 'user cannot remove source branch', :sidekiq_might_not_need_inline do expect(page).not_to have_field('remove-source-branch-input') - expect(page).to have_content('The source branch will be deleted') + expect(page).to have_content('Deletes the source branch') end end diff --git a/spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap b/spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap index 5981d2d7849..56a0218b374 100644 --- a/spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap +++ b/spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap @@ -50,7 +50,7 @@ exports[`MRWidgetAutoMergeEnabled when graphql is disabled template should have - The source branch will not be deleted + Does not delete the source branch - The source branch will not be deleted + Does not delete the source branch { const normalizedText = wrapper.text().replace(/\s+/g, ' '); - expect(normalizedText).toContain('The source branch will be deleted'); - expect(normalizedText).not.toContain('The source branch will not be deleted'); + expect(normalizedText).toContain('Deletes the source branch'); + expect(normalizedText).not.toContain('Does not delete the source branch'); }); it('should not show delete source branch button when user not able to delete source branch', () => { diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_merging_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_merging_spec.js index b6c16958993..e6b2e9fa176 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_merging_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_merging_spec.js @@ -42,7 +42,7 @@ describe('MRWidgetMerging', () => { .trim() .replace(/\s\s+/g, ' ') .replace(/[\r\n]+/g, ' '), - ).toEqual('The changes will be merged into branch'); + ).toEqual('Merges changes into branch'); expect(wrapper.find('a').attributes('href')).toBe('/branch-path'); }); diff --git a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js index 844e7330679..550f156d095 100644 --- a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js +++ b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js @@ -543,7 +543,7 @@ describe('MrWidgetOptions', () => { nextTick(() => { const tooltip = wrapper.find('[data-testid="question-o-icon"]'); - expect(wrapper.text()).toContain('The source branch will be deleted'); + expect(wrapper.text()).toContain('Deletes the source branch'); expect(tooltip.attributes('title')).toBe( 'A user with write access to the source branch selected this option', ); @@ -559,7 +559,7 @@ describe('MrWidgetOptions', () => { nextTick(() => { expect(wrapper.text()).toContain('The source branch has been deleted'); - expect(wrapper.text()).not.toContain('The source branch will be deleted'); + expect(wrapper.text()).not.toContain('Deletes the source branch'); done(); }); diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb index 423e19c3971..641c6a2cd91 100644 --- a/spec/requests/api/settings_spec.rb +++ b/spec/requests/api/settings_spec.rb @@ -612,5 +612,46 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do expect(json_response.slice(*settings.keys)).to eq(settings) end end + + context 'Sentry settings' do + let(:settings) do + { + sentry_enabled: true, + sentry_dsn: 'http://sentry.example.com', + sentry_clientside_dsn: 'http://sentry.example.com', + sentry_environment: 'production' + } + end + + let(:attribute_names) { settings.keys.map(&:to_s) } + + it 'includes the attributes in the API' do + get api('/application/settings', admin) + + expect(response).to have_gitlab_http_status(:ok) + attribute_names.each do |attribute| + expect(json_response.keys).to include(attribute) + end + end + + it 'allows updating the settings' do + put api('/application/settings', admin), params: settings + + expect(response).to have_gitlab_http_status(:ok) + settings.each do |attribute, value| + expect(ApplicationSetting.current.public_send(attribute)).to eq(value) + end + end + + context 'missing sentry_dsn value when sentry_enabled is true' do + it 'returns a blank parameter error message' do + put api('/application/settings', admin), params: { sentry_enabled: true } + + expect(response).to have_gitlab_http_status(:bad_request) + message = json_response['message'] + expect(message["sentry_dsn"]).to include(a_string_matching("can't be blank")) + end + end + end end end diff --git a/spec/lib/gitlab/sidekiq_cluster_spec.rb b/spec/sidekiq_cluster/sidekiq_cluster_spec.rb similarity index 98% rename from spec/lib/gitlab/sidekiq_cluster_spec.rb rename to spec/sidekiq_cluster/sidekiq_cluster_spec.rb index 3c6ea054968..1d2b47e78ce 100644 --- a/spec/lib/gitlab/sidekiq_cluster_spec.rb +++ b/spec/sidekiq_cluster/sidekiq_cluster_spec.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true -require 'fast_spec_helper' require 'rspec-parameterized' -RSpec.describe Gitlab::SidekiqCluster do +require_relative '../../sidekiq_cluster/sidekiq_cluster' + +RSpec.describe Gitlab::SidekiqCluster do # rubocop:disable RSpec/FilePath describe '.trap_signals' do it 'traps the given signals' do expect(described_class).to receive(:trap).ordered.with(:INT) diff --git a/spec/support/flaky_tests.rb b/spec/support/flaky_tests.rb index c619f1412f1..535df64f890 100644 --- a/spec/support/flaky_tests.rb +++ b/spec/support/flaky_tests.rb @@ -2,7 +2,7 @@ return unless ENV['CI'] return unless ENV['SKIP_FLAKY_TESTS_AUTOMATICALLY'] == "true" -return if ENV['CI_MERGE_REQUEST_LABELS'].include?(/pipeline:run-flaky-tests/) +return if ENV['CI_MERGE_REQUEST_LABELS'].to_s.include?('pipeline:run-flaky-tests') require_relative '../tooling/rspec_flaky/report' diff --git a/spec/tooling/quality/test_level_spec.rb b/spec/tooling/quality/test_level_spec.rb index 40eb93c838b..94fa9d682e1 100644 --- a/spec/tooling/quality/test_level_spec.rb +++ b/spec/tooling/quality/test_level_spec.rb @@ -49,7 +49,7 @@ RSpec.describe Quality::TestLevel do context 'when level is integration' do it 'returns a pattern' do expect(subject.pattern(:integration)) - .to eq("spec/{controllers,mailers,requests}{,/**/}*_spec.rb") + .to eq("spec/{commands,controllers,mailers,requests}{,/**/}*_spec.rb") end end @@ -131,7 +131,7 @@ RSpec.describe Quality::TestLevel do context 'when level is integration' do it 'returns a regexp' do expect(subject.regexp(:integration)) - .to eq(%r{spec/(controllers|mailers|requests)}) + .to eq(%r{spec/(commands|controllers|mailers|requests)}) end end @@ -204,6 +204,10 @@ RSpec.describe Quality::TestLevel do expect(subject.level_for('spec/mailers/abuse_report_mailer_spec.rb')).to eq(:integration) end + it 'returns the correct level for an integration test in a subfolder' do + expect(subject.level_for('spec/commands/sidekiq_cluster/cli.rb')).to eq(:integration) + end + it 'returns the correct level for a system test' do expect(subject.level_for('spec/features/abuse_report_spec.rb')).to eq(:system) end diff --git a/tooling/quality/test_level.rb b/tooling/quality/test_level.rb index 57eb19517ea..5fbaad073c0 100644 --- a/tooling/quality/test_level.rb +++ b/tooling/quality/test_level.rb @@ -54,6 +54,7 @@ module Quality tooling ], integration: %w[ + commands controllers mailers requests